2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-22 09:57:34 +00:00

postfix-2.11-20130927

This commit is contained in:
Wietse Venema 2013-09-27 00:00:00 -05:00 committed by Viktor Dukhovni
parent 26eddc2f78
commit 2bf7cc9990
41 changed files with 1374 additions and 880 deletions

8
postfix/.indent.pro vendored
View File

@ -1,3 +1,4 @@
-TMDB_val
-TABOUNCE
-TADDR_MATCH_LIST
-TADDR_PATTERN
@ -119,8 +120,8 @@
-TDSN_BUF
-TDSN_SPLIT
-TDSN_STAT
-TEC_KEY
-TEC_GROUP
-TEC_KEY
-TEDIT_FILE
-TEVENT_MASK
-TEVP_PKEY
@ -176,6 +177,7 @@
-TMATCH_OPS
-TMBLOCK
-TMBOX
-TMDB_txn
-TMILTER
-TMILTER8
-TMILTERS
@ -330,10 +332,10 @@
-TWATCHDOG
-TWATCH_FD
-TX509
-TX509V3_CTX
-TX509_EXTENSION
-TX509_NAME
-TX509_STORE_CTX
-TX509V3_CTX
-TXSASL_CLIENT
-TXSASL_CLIENT_CREATE_ARGS
-TXSASL_CLIENT_IMPL
@ -358,9 +360,9 @@
-Tsasl_secret_t
-Tsfsistat
-Tsize_t
-Tssize_t
-Tssl_cipher_stack_t
-Tssl_comp_stack_t
-Tssize_t
-Ttime_t
-Tx509_extension_stack_t
-Tx509_stack_t

View File

@ -18719,10 +18719,11 @@ Apologies for any names omitted.
20130615
Interoperability: turn on SHA-2XX digests by force. This
improves interoperability with clients and servers with
ancient OpenSSL versions and that that prematurely deploy
SHA-2 certificates. Viktor Dukhovni. File: tls/tls_misc.c
TLS Interoperability: turn on SHA-2 digests by force. This
improves interoperability with clients and servers that
deploy SHA-2 digests without the required support for
TLSv1.2-style digest negotiation. Based on patch by Viktor
Dukhovni. Files: tls/tls_client.c, tls/tls_server.c.
20130616
@ -18912,3 +18913,33 @@ Apologies for any names omitted.
src/smtp/smtp_addr.h, src/smtp/smtp_connect.c,
src/smtp/smtp_params.c, src/smtp/smtp_tls_policy.c,
src/tls/tls.h, src/tls/tls_dane.c.
20130826
Documentation: re-ordered STRESS_README, now that all
supported releases have stress-adaptive behavior built in.
File: proto/STRESS_README.html.
20130903
Cleanup: made the default_database_type compile-time
configurable. Files: util/sys_defs.h, makedefs, proto/INSTALL.
20130916
Feature: reject_known_sender_login_mismatch, which applies
reject_sender_login_mismatch only to MAIL FROM addresses
that are known in $smtpd_sender_login_maps. Viktor & Wietse.
Files: mantools/postlink, proto/SASL_README.html,
proto/postconf.proto, global/mail_params.h, smtpd/smtpd_check.c.
20130927
Cleanup: no more LMDB "database full" errors. Postfix now
requires LMDB >= 0.9.8 which supports on-the-fly database
resizing. When a database becomes full, its size limit is
automatically doubled, and other processes automatically
pick up the new database size limit. Files: util/dict.h,
util/dict_open.c, util/dict_alloc.c, util/dict_lmdb.c,
postmap/postmap.c, postalias/postalias.c, proto/LMDB_README.html,
proto/postconf.proto.

View File

@ -194,31 +194,33 @@ IMPORTANT: Be sure to get the quotes right. These details matter a lot.
Parameters whose defaults can be specified in this way are:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
|MMaaccrroo nnaammee |ddeeffaauulltt vvaalluuee ffoorr|ttyyppiiccaall ddeeffaauulltt |
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|DEF_COMMAND_DIR |command_directory|/usr/sbin |
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
|MMaaccrroo nnaammee |ddeeffaauulltt vvaalluuee ffoorr |ttyyppiiccaall ddeeffaauulltt |
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|DEF_COMMAND_DIR |command_directory |/usr/sbin |
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|DEF_CONFIG_DIR |config_directory |/etc/postfix |
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|DEF_DB_TYPE |default_database_type|hash |
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|DEF_DAEMON_DIR |daemon_directory |/usr/libexec/postfix|
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|DEF_DATA_DIR |data_directory |/var/lib/postfix |
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|DEF_MAILQ_PATH |mailq_path |/usr/bin/mailq |
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|DEF_HTML_DIR |html_directory |no |
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|DEF_MANPAGE_DIR |manpage_directory|/usr/local/man |
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|DEF_MANPAGE_DIR |manpage_directory |/usr/local/man |
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|DEF_NEWALIAS_PATH|newaliases_path |/usr/bin/newaliases |
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|DEF_QUEUE_DIR |queue_directory |/var/spool/postfix |
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|DEF_README_DIR |readme_directory |no |
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|DEF_SENDMAIL_PATH|sendmail_path |/usr/sbin/sendmail |
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
Note: the data_directory parameter (for caches and pseudo-random numbers) was
introduced with Postfix version 2.5.
@ -247,9 +249,11 @@ The following is an extensive list of names and values.
| |at compile time: |
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|| |Do not build with Berkeley DB support. By |
||-DNO_DB |default, Berkeley DB support is compiled in on|
|| |platforms that are known to support this |
|| |feature. |
|| |default, Berkeley DB support is compiled in on|
||-DNO_DB |platforms that are known to support this |
|| |feature. If you override this, then you |
|| |probably should also override DEF_DB_TYPE as |
|| |described in section 4.4. |
|_|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|| |Do not build with Solaris /dev/poll support. |
||-DNO_DEVPOLL |By default, /dev/poll support is compiled in |

View File

@ -4,14 +4,6 @@ PPoossttffiixx OOppeennLLDDAAPP LLMMDDBB HHoowwttoo
IInnttrroodduuccttiioonn
WWaarrnniinngg:: LLMMDDBB ddaattaabbaasseess hhaavvee aa sshhooww--ssttooppppeerr bbuugg:: tthheeyy ddoo nnoott ggrrooww bbeeyyoonndd aa
ssppeecciiffiieedd lliimmiitt,, aanndd iinnttrroodduuccee aa ""ddaattaabbaassee ffuullll"" hhaarrdd eerrrroorr ccoonnddiittiioonn tthhaatt
ddooeess nnoott eexxiisstt wwiitthh aannyy ootthheerr PPoossttffiixx ddaattaabbaassee ttyyppee.. TThhiiss iiss aa pprroobblleemm ffoorr
pprrooggrraammss wwhhoossee ddaattaabbaassee ggrroowwss wwiitthh ssyysstteemm llooaadd.. EExxaammpplleess aarree ppoossttssccrreeeenn((88)),,
ggrreeyylliissttiinngg,, vveerriiffyy((88)) aanndd ttllssmmggrr((88))..
YYoouu hhaavvee bbeeeenn wwaarrnneedd..
Postfix uses databases of various kinds to store and look up information.
Postfix databases are specified as "type:name". OpenLDAP LMDB implements the
Postfix database type "lmdb". The name of a Postfix OpenLDAP LMDB database is
@ -30,14 +22,14 @@ This document describes:
BBuuiillddiinngg PPoossttffiixx wwiitthh OOppeennLLDDAAPP LLMMDDBB ssuuppppoorrtt
Postfix normally does not enable OpenLDAP LMDB support. To build Postfix after
you installed OpenLDAP LMDB from source code, use something like:
Postfix normally does not enable OpenLDAP LMDB support. To build Postfix with
OpenLDAP LMDB support, use something like:
% make makefiles CCARGS="-DHAS_LMDB -I/usr/local/include" \
AUXLIBS="-L/usr/local/lib -llmdb"
% make
Solaris needs this:
Solaris may need this:
% make makefiles CCARGS="-DHAS_LMDB -I/usr/local/include" \
AUXLIBS="-R/usr/local/lib -L/usr/local/lib -llmdb"
@ -47,12 +39,12 @@ The exact pathnames depend on how OpenLDAP LMDB was installed.
CCoonnffiigguurree LLMMDDBB sseettttiinnggss
Postfix provides a configuration parameter that controls how large an OpenLDAP
LMDB database may grow.
Postfix provides one configuration parameter that controls OpenLDAP LMDB
database behavior.
* lmdb_map_size (default: 16 MBytes per table). This setting controls how
large any OpenLDAP LMDB database may grow. It must be set large enough to
accommodate the largest table that Postfix will use.
* lmdb_map_size (default: 16777216). This setting specifies the initial
OpenLDAP LMDB database size limit in bytes. Each time a database becomes
full, its size limit is doubled.
MMiissssiinngg pptthhrreeaadd lliibbrraarryy ttrroouubbllee
@ -72,81 +64,17 @@ information is available at http://highlandsun.com/hyc/mdb/.
UUnneexxppeecctteedd ffaaiilluurree mmooddeess ooff PPoossttffiixx LLMMDDBB ddaattaabbaasseess..
As documented below, conversion to LMDB introduces a number of failure modes
that don't exist with other Postfix databases.
UUnneexxppeecctteedd ppoossttmmaapp((11))//ppoossttaalliiaass((11)) ""ddaattaabbaassee ffuullll"" eerrrroorrss..
Problem:
The "postmap lmdb:filename" command fails with an MDB_MAP_FULL error. This
problem does not exist with other Postfix databases.
Background:
LMDB databases have a hard size limit (configured with the lmdb_map_size
configuration parameter).
When executing "postmap lmdb:filename", the Postfix LMDB database client
stores the new data in a transaction which takes up space in addition to
the existing data, and commits the transaction when it closes the database.
Only then can the space for old data be reused.
Impact:
This failure does not affect Postfix availability, because the old data
still exists in the database.
Mitigation:
When the postmap(1) or postalias(1) command fails with an MDB_MAP_FULL
error, it expands the database file size to the current LMDB map size limit
before terminating.
When the postmap(1) or postalias(1) command opens an LMDB file larger than
lmdb_map_size/3, it logs a warning and uses a larger size limit instead:
warning: filename.lmdb: file size 15024128 >= (lmdb map size limit
16777216)/3 -- using a larger map size limit
The two steps above can be used to automate recovery and avoid the need for
human intervention. Just repeat "postmap lmdb:filename" (up to some limit).
After each failure it will use a 3x larger size limit, and eventually the
"database full" error will disappear.
Prevention:
Monitor your LMDB files and make sure that lmdb_map_size > 3x the largest
LMDB file size.
UUnneexxppeecctteedd PPoossttffiixx ddaaeemmoonn ""ddaattaabbaassee ffuullll"" eerrrroorrss..
Problem:
Postfix daemon programs fail with "database full" errors, such as
postscreen(8), tlsmgr(8) or verify(8). This problem does not exist with
other Postfix databases.
Impact:
This failure temporarily affects Postfix availability. The daemon restarts
automatically and tries to open the database again as described next.
Mitigation:
When a Postfix daemon opens an LMDB file larger than lmdb_map_size/3, it
logs a warning and uses a larger size limit instead:
warning: filename.lmdb: file size 15024128 >= (lmdb map size limit
16777216)/3 -- using a larger map size limit
This can be used to automate recovery and avoid the need for human
intervention. Each time the daemon runs into a "database full" error, it
restarts and uses a 3x larger size limit. The "database full" error will
disappear, at least for a while.
Prevention:
Monitor your LMDB files and make sure that lmdb_map_size > 3x the largest
LMDB file size.
that don't exist with other Postfix databases. Some failure modes have been
eliminated on the course of time. The writeup below reflects the status as of
of LMDB 0.9.8.
NNoonn--oobbvviioouuss rreeccoovveerryy wwiitthh ppoossttmmaapp((11))//ppoossttaalliiaass((11))//ttllssmmggrr((88)) ffrroomm aa ccoorrrruupptteedd
ddaattaabbaassee..
Problem:
You cannot rebuild a corrupted LMDB database simply by running postmap(1)
or postalias(1), or by waiting until the tlsmgr(8) daemon restarts
automatically. This problem does not exist with other Postfix databases.
You cannot rebuild a corrupted LMDB database simply by re-running postmap
(1) or postalias(1), or by waiting until the tlsmgr(8) daemon restarts.
This problem does not exist with other Postfix databases.
Background:
The Postfix LMDB database client does not truncate the database file.
@ -158,9 +86,9 @@ Impact:
Postfix does not process mail until someone fixes the problem.
Recovery:
First delete the ".lmdb" file by hand, then rebuild the file with the
First delete the ".lmdb" file by hand. Then, rebuild the file with the
postmap(1) or postalias(1) command, or wait until the tlsmgr(8) daemon
restarts automatically.
restarts.
Prevention:
Arrange your file systems such that they never run out of free space.

View File

@ -896,7 +896,8 @@ With this, the reject_sender_login_mismatch restriction above will reject the
sender address in the MAIL FROM command if smtpd_sender_login_maps does not
specify the SMTP client's login name as an owner of that address.
See also reject_authenticated_sender_login_mismatch and
See also reject_authenticated_sender_login_mismatch,
reject_known_sender_login_mismatch, and
reject_unauthenticated_sender_login_mismatch for additional control over the
SASL login name and the envelope sender.

View File

@ -13,11 +13,11 @@ stress-adaptive behavior, and for earlier Postfix versions that don't.
Topics covered in this document:
* Symptoms of Postfix SMTP server overload
* Automatic stress-adaptive behavior
* Service more SMTP clients at the same time
* Spend less time per SMTP client
* Disconnect suspicious SMTP clients
* Temporary measures for older Postfix releases
* Automatic stress-adaptive behavior
* Detecting support for stress-adaptive behavior
* Forcing stress-adaptive behavior on or off
* Other measures to off-load zombies
@ -65,13 +65,101 @@ Symptoms of Postfix SMTP server overload are:
Oct 3 20:39:27 spike postfix/master[28905]: warning: to avoid this
condition, increase the process count in master.cf or reduce the
service time per client
Oct 3 20:39:27 spike postfix/master[28905]: warning: see
http://www.postfix.org/STRESS_README.html for examples of
stress-adapting configuration settings
Legitimate mail that doesn't get through during an episode of Postfix SMTP
server overload is not necessarily lost. It should still arrive once the
situation returns to normal, as long as the overload condition is temporary.
AAuuttoommaattiicc ssttrreessss--aaddaappttiivvee bbeehhaavviioorr
Postfix version 2.5 introduces automatic stress-adaptive behavior. It works as
follows. When a "public" network service such as the SMTP server runs into an
"all server ports are busy" condition, the Postfix master(8) daemon logs a
warning, restarts the service (without interrupting existing network sessions),
and runs the service with "-o stress=yes" on the server process command line:
80821 ?? S 0:00.24 smtpd -n smtp -t inet -u -c -o stress=yes
Normally, the Postfix master(8) daemon runs such a service with "-o stress=" on
the command line (i.e. with an empty parameter value):
83326 ?? S 0:00.28 smtpd -n smtp -t inet -u -c -o stress=
Services that have local access only never have "-o stress" parameters on the
command line. This includes services internal to Postfix such as the queue
manager, and services that listen on a loopback interface only, such as after-
filter SMTP services.
The "stress" parameter value is the key to making main.cf parameter settings
stress adaptive. The following settings are the default with Postfix 2.6 and
later.
1 smtpd_timeout = ${stress?10}${stress:300}s
2 smtpd_hard_error_limit = ${stress?1}${stress:20}
3 smtpd_junk_command_limit = ${stress?1}${stress:100}
4 # Parameters added after Postfix 2.6:
5 smtpd_per_record_deadline = ${stress?yes}${stress:no}
6 smtpd_starttls_timeout = ${stress?10}${stress:300}s
7 address_verify_poll_count = ${stress?1}${stress:3}
Translation:
* Line 1: under conditions of stress, use an smtpd_timeout value of 10
seconds instead of the default 300 seconds. Experience on the postfix-users
list from a variety of sysadmins shows that reducing the "normal"
smtpd_timeout to 60s is unlikely to affect legitimate clients. However, it
is unlikely to become the Postfix default because it's not RFC compliant.
Setting smtpd_timeout to 10s or even 5s under stress will still allow most
legitimate clients to connect and send mail, but may delay mail from some
clients. No mail should be lost, as long as this measure is used only
temporarily.
* Line 2: under conditions of stress, use an smtpd_hard_error_limit of 1
instead of the default 20. This helps by disconnecting clients after a
single error, giving other clients a chance to connect. However, this may
cause significant delays with legitimate mail, such as a mailing list that
contains a few no-longer-active user names that didn't bother to
unsubscribe. No mail should be lost, as long as this measure is used only
temporarily.
* Line 3: under conditions of stress, use an smtpd_junk_command_limit of 1
instead of the default 100. This prevents clients from keeping connections
open by repeatedly sending HELO, EHLO, NOOP, RSET, VRFY or ETRN commands.
* Line 5: under conditions of stress, change the behavior of smtpd_timeout
and smtpd_starttls_timeout, from a time limit per read or write system
call, to a time limit to send or receive a complete record (an SMTP command
line, SMTP response line, SMTP message content line, or TLS protocol
message).
* Line 6: under conditions of stress, reduce the time limit for TLS protocol
handshake messages to 10 seconds, from the default value of 300 seconds.
See also the smtpd_timeout discussion above.
* Line 7: under conditions of stress, do not wait up to 6 seconds for the
completion of an address verification probe. If the result is not already
in the address verification cache, reply immediately with
$unverified_recipient_tempfail_action or
$unverified_sender_tempfail_action. No mail should be lost, as long as this
measure is used only temporarily.
The syntax of ${name?value} and ${name:value} is explained at the beginning of
the postconf(5) manual page.
NOTE: Please keep in mind that the stress-adaptive feature is a fairly
desperate measure to keep ssoommee legitimate mail flowing under overload
conditions. If a site is reaching the SMTP server process limit when there
isn't an attack or bot flood occurring, then either the process limit needs to
be raised or more hardware needs to be added.
SSeerrvviiccee mmoorree SSMMTTPP cclliieennttss aatt tthhee ssaammee ttiimmee
This section and the ones that follow discuss permanent measures against mail
server overload.
One measure to avoid the "all server processes busy" condition is to service
more SMTP clients simultaneously. For this you need to increase the number of
Postfix SMTP server processes. This will improve the responsiveness for remote
@ -256,72 +344,6 @@ With these measures, no mail should be lost, as long as these measures are used
only temporarily. The next section of this document introduces a way to
automate this process.
AAuuttoommaattiicc ssttrreessss--aaddaappttiivvee bbeehhaavviioorr
Postfix version 2.5 introduces automatic stress-adaptive behavior. This is also
available as a source code patch for Postfix versions 2.4 and 2.3 from the
mirrors listed at http://www.postfix.org/download.html.
It works as follows. When a "public" network service such as the SMTP server
runs into an "all server ports are busy" condition, the Postfix master(8)
daemon logs a warning, restarts the service (without interrupting existing
network sessions), and runs the service with "-o stress=yes" on the server
process command line:
80821 ?? S 0:00.24 smtpd -n smtp -t inet -u -c -o stress=yes
Normally, the Postfix master(8) daemon runs such a service with "-o stress=" on
the command line (i.e. with an empty parameter value):
83326 ?? S 0:00.28 smtpd -n smtp -t inet -u -c -o stress=
Services that have local access only never have "-o stress" parameters on the
command line. This includes services internal to Postfix such as the queue
manager, and services that listen on a loopback interface only, such as after-
filter SMTP services.
The "stress" parameter value is the key to making main.cf parameter settings
stress adaptive. The following settings are the default with Postfix 2.6 and
later. With earlier Postfix versions that have stress-adaptive support, append
the lines below to the main.cf file and issue a "postfix reload" command:
1 smtpd_timeout = ${stress?10}${stress:300}s
2 smtpd_hard_error_limit = ${stress?1}${stress:20}
3 smtpd_junk_command_limit = ${stress?1}${stress:100}
Translation:
* Line 1: under conditions of stress, use an smtpd_timeout value of 10
seconds instead of the default 300 seconds. Experience on the postfix-users
list from a variety of sysadmins shows that reducing the "normal"
smtpd_timeout to 60s is unlikely to affect legitimate clients. However, it
is unlikely to become the Postfix default because it's not RFC compliant.
Setting smtpd_timeout to 10s (line 2 below) or even 5s under stress will
still allow most legitimate clients to connect and send mail, but may delay
mail from some clients. No mail should be lost, as long as this measure is
used only temporarily.
* Line 2: under conditions of stress, use an smtpd_hard_error_limit of 1
instead of the default 20. This helps by disconnecting clients after a
single error, giving other clients a chance to connect. However, this may
cause significant delays with legitimate mail, such as a mailing list that
contains a few no-longer-active user names that didn't bother to
unsubscribe. No mail should be lost, as long as this measure is used only
temporarily.
* Line 3: under conditions of stress, use an smtpd_junk_command_limit of 1
instead of the default 100. This prevents clients from keeping idle
connections open by repeatedly sending NOOP or RSET commands.
The syntax of ${name?value} and ${name:value} is explained at the beginning of
the postconf(5) manual page.
NOTE: Please keep in mind that the stress-adaptive feature is a fairly
desperate measure to keep ssoommee legitimate mail flowing under overload
conditions. If a site is reaching the SMTP server process limit when there
isn't an attack or bot flood occurring, then either the process limit needs to
be raised or more hardware needs to be added.
DDeetteeccttiinngg ssuuppppoorrtt ffoorr ssttrreessss--aaddaappttiivvee bbeehhaavviioorr
To find out if your Postfix installation supports stress-adaptive behavior, use

View File

@ -14,6 +14,14 @@ specifies the release date of a stable release or snapshot release.
If you upgrade from Postfix 2.9 or earlier, read RELEASE_NOTES-2.10
before proceeding.
Major changes with snapshot 20130927
====================================
Postfix now handles LMDB "database full" errors automatically. When
a database becomes full, its size limit is doubled, and other
processes automatically pick up the new size limit. The lmdb_map_size
parameter is now mostly irrelevant, and may be removed in the future.
Major changes with snapshot 20130602
====================================

View File

@ -16,6 +16,8 @@ Wish list:
Begin code revision, after DANE support stabilizes. This
should be one pass that changes only names and no code.
recipient_delimiters = $recipient_delimiter for BC
All source code must specify its original author and
license statement. Some code modules specify Lutz Jaenicke
as the original author and fall under his liberal license.

View File

@ -310,6 +310,9 @@ default</th> </tr>
<tr> <td>DEF_CONFIG_DIR</td> <td><a href="postconf.5.html#config_directory">config_directory</a></td>
<td>/etc/postfix</td> </tr>
<tr> <td>DEF_DB_TYPE</td> <td><a href="postconf.5.html#default_database_type">default_database_type</a></td>
<td>hash</td> </tr>
<tr> <td>DEF_DAEMON_DIR</td> <td><a href="postconf.5.html#daemon_directory">daemon_directory</a></td>
<td>/usr/libexec/postfix</td> </tr>
@ -376,7 +379,9 @@ off Postfix features at compile time:</td> </tr>
<tr> <td> </td> <td> -DNO_DB </td> <td> Do not build with Berkeley
DB support. By default, Berkeley DB support is compiled in on
platforms that are known to support this feature. </td> </tr>
platforms that are known to support this feature. If you override
this, then you probably should also override DEF_DB_TYPE as described
in section 4.4. </td> </tr>
<tr> <td> </td> <td> -DNO_DEVPOLL </td> <td> Do not build with
Solaris <tt>/dev/poll</tt> support. By default, <tt>/dev/poll</tt>

View File

@ -19,14 +19,6 @@
<h2>Introduction</h2>
<blockquote> <p> <strong> Warning: LMDB databases have a show-stopper
bug: they do not grow beyond a specified limit, and introduce a
"database full" hard error condition that does not exist with any
other Postfix database type. This is a problem for programs whose
database grows with system load. Examples are <a href="postscreen.8.html">postscreen(8)</a>,
greylisting, <a href="verify.8.html">verify(8)</a> and <a href="tlsmgr.8.html">tlsmgr(8)</a>. </strong> </p> <p> <strong>
You have been warned. </strong> </p> </blockquote>
<p> Postfix uses databases of various kinds to store and look up
information. Postfix databases are specified as "type:name".
OpenLDAP LMDB implements the Postfix database type "lmdb".
@ -52,9 +44,8 @@ don't exist with other Postfix databases. </p>
<h2><a name="with_lmdb">Building Postfix with OpenLDAP LMDB support</a></h2>
<p> Postfix normally does not enable OpenLDAP LMDB support.
To build Postfix after you installed OpenLDAP LMDB from
source code, use something like: </p>
<p> Postfix normally does not enable OpenLDAP LMDB support. To
build Postfix with OpenLDAP LMDB support, use something like: </p>
<blockquote>
<pre>
@ -64,7 +55,7 @@ source code, use something like: </p>
</pre>
</blockquote>
<p> Solaris needs this: </p>
<p> Solaris may need this: </p>
<blockquote>
<pre>
@ -78,15 +69,14 @@ source code, use something like: </p>
<h2><a name="configure">Configure LMDB settings</a></h2>
<p> Postfix provides a configuration parameter that controls how
large an OpenLDAP LMDB database may grow. </p>
<p> Postfix provides one configuration parameter that controls
OpenLDAP LMDB database behavior. </p>
<ul>
<li> <p> <a href="postconf.5.html#lmdb_map_size">lmdb_map_size</a> (default: 16 MBytes per table). This setting
controls how large any OpenLDAP LMDB database may grow. It must be
set large enough to accommodate the largest table that Postfix will
use. </p>
<li> <p> <a href="postconf.5.html#lmdb_map_size">lmdb_map_size</a> (default: 16777216). This setting specifies
the initial OpenLDAP LMDB database size limit in bytes. Each time
a database becomes full, its size limit is doubled. </p>
</ul>
@ -119,15 +109,17 @@ More information is available at
databases. </a> </h2>
<p> As documented below, conversion to LMDB introduces a number of
failure modes that don't exist with other Postfix databases. </p>
failure modes that don't exist with other Postfix databases. Some
failure modes have been eliminated on the course of time.
The writeup below reflects the status as of of LMDB 0.9.8. </p>
<!--
<p> <strong>Unexpected <a href="postmap.1.html">postmap(1)</a>/<a href="postalias.1.html">postalias(1)</a> "database full"
errors. </strong></p>
<dl>
<!--
<dt> Problem: </dt> <dd> <p> The "postmap <a href="LMDB_README.html">lmdb</a>:filename" command
fails with an MDB_TXN_FULL error. This problem does not exist with
other Postfix databases. </p> </dd>
@ -146,8 +138,6 @@ structures should share the same storage pool so that they can scale
with the database size, and so that all "out of storage" errors are
resolved by increasing the database size. </p> </dd>
-->
<dt> Problem: </dt> <dd> <p> The "postmap <a href="LMDB_README.html">lmdb</a>:filename" command
fails with an MDB_MAP_FULL error. This problem does not exist with
other Postfix databases. </p> </dd>
@ -177,23 +167,25 @@ availability, because the old data still exists in the database.
MDB_MAP_FULL error, it expands the database file size to the current
LMDB map size limit before terminating. </p>
<p> When the <a href="postmap.1.html">postmap(1)</a> or <a href="postalias.1.html">postalias(1)</a> command opens an LMDB file
larger than <a href="postconf.5.html#lmdb_map_size">lmdb_map_size</a>/3, it logs a warning and uses a larger
size limit instead: </p>
<p> Next, when you re-run the <a href="postmap.1.html">postmap(1)</a> or <a href="postalias.1.html">postalias(1)</a> command,
it discovers that the LMDB file is larger than <a href="postconf.5.html#lmdb_map_size">lmdb_map_size</a>/3,
logs a warning, and uses a larger LMDB map size limit instead: </p>
<p> <tt> warning: <i>filename</i>.<a href="LMDB_README.html">lmdb</a>: file size 15024128 &ge;
(lmdb map size limit 16777216)/3 -- using a larger map size limit</tt>
</p>
(lmdb map size limit 16777216)/3<br> warning: <i>filename</i>.<a href="LMDB_README.html">lmdb</a>:
using map size limit 45072384</tt> </p>
<p> The two steps above can be used to automate recovery and avoid
the need for human intervention. Just repeat "postmap <a href="LMDB_README.html">lmdb</a>:filename"
(up to some limit). After each failure it will use a 3x larger
size limit, and eventually the "database full" error will disappear.
</p>
<p> By repeating the two steps above you can automate recovery and
avoid the need for human intervention. Just repeat "postmap
<a href="LMDB_README.html">lmdb</a>:filename" (up to some limit). After each failure it will use
a 3x larger size limit, and eventually the "database full" error
should disappear. This fails only when the disk is full or when
the LMDB map size limit would exceed the memory address space size
limit. </p>
<dt> Prevention: </dt> <dd> <p> Monitor your LMDB files and make
sure that <a href="postconf.5.html#lmdb_map_size">lmdb_map_size</a> &gt; 3x the largest LMDB file size. </p>
</dd> </dl>
sure that in <a href="postconf.5.html">main.cf</a>, <a href="postconf.5.html#lmdb_map_size">lmdb_map_size</a> &gt; 3x the largest LMDB file
size. </p> </dd> </dl>
<p> <strong>Unexpected Postfix daemon "database full" errors.
</strong></p>
@ -214,8 +206,8 @@ file larger than <a href="postconf.5.html#lmdb_map_size">lmdb_map_size</a>/3, it
larger size limit instead: </p>
<p> <tt> warning: <i>filename</i>.<a href="LMDB_README.html">lmdb</a>: file size 15024128 &ge;
(lmdb map size limit 16777216)/3 -- using a larger map size limit</tt>
</p>
(lmdb map size limit 16777216)/3 <br>warning: <i>filename</i>.<a href="LMDB_README.html">lmdb</a>:
using map size limit 45072384</tt> </p>
<p> This can be used to automate recovery and avoid the need for
human intervention. Each time the daemon runs into a "database full"
@ -226,14 +218,16 @@ full" error will disappear, at least for a while. </p>
sure that <a href="postconf.5.html#lmdb_map_size">lmdb_map_size</a> &gt; 3x the largest LMDB file size. </p>
</dd> </dl>
-->
<p> <strong>Non-obvious recovery with <a href="postmap.1.html">postmap(1)</a>/<a href="postalias.1.html">postalias(1)</a>/<a href="tlsmgr.8.html">tlsmgr(8)</a>
from a corrupted database. </strong></p>
<dl>
<dt> Problem: </dt> <dd> <p> You cannot rebuild a corrupted LMDB
database simply by running <a href="postmap.1.html">postmap(1)</a> or <a href="postalias.1.html">postalias(1)</a>, or by waiting
until the <a href="tlsmgr.8.html">tlsmgr(8)</a> daemon restarts automatically. This problem
database simply by re-running <a href="postmap.1.html">postmap(1)</a> or <a href="postalias.1.html">postalias(1)</a>, or by
waiting until the <a href="tlsmgr.8.html">tlsmgr(8)</a> daemon restarts. This problem
does not exist with other Postfix databases. </p> </dd>
<dt> Background: </dt> <dd> <p> The Postfix LMDB database client
@ -245,9 +239,9 @@ That is obviously not possible with a corrupted database file. </p>
<dt> Impact: </dt> <dd> <p> Postfix does not process mail until
someone fixes the problem. </p> </dd>
<dt> Recovery: </dt> <dd> <p> First delete the ".lmdb" file by hand,
then rebuild the file with the <a href="postmap.1.html">postmap(1)</a> or <a href="postalias.1.html">postalias(1)</a> command,
or wait until the <a href="tlsmgr.8.html">tlsmgr(8)</a> daemon restarts automatically. </p>
<dt> Recovery: </dt> <dd> <p> First delete the ".lmdb" file by hand.
Then, rebuild the file with the <a href="postmap.1.html">postmap(1)</a> or <a href="postalias.1.html">postalias(1)</a> command,
or wait until the <a href="tlsmgr.8.html">tlsmgr(8)</a> daemon restarts. </p>
</dd>
<dt> Prevention: </dt> <dd>

View File

@ -1450,7 +1450,8 @@ restriction above will reject the sender address in the MAIL FROM
command if <code><a href="postconf.5.html#smtpd_sender_login_maps">smtpd_sender_login_maps</a></code> does not specify
the SMTP client's login name as an owner of that address. </p>
<p> See also <code><a href="postconf.5.html#reject_authenticated_sender_login_mismatch">reject_authenticated_sender_login_mismatch</a></code> and
<p> See also <code><a href="postconf.5.html#reject_authenticated_sender_login_mismatch">reject_authenticated_sender_login_mismatch</a></code>,
<code><a href="postconf.5.html#reject_known_sender_login_mismatch">reject_known_sender_login_mismatch</a></code>, and
<code><a href="postconf.5.html#reject_unauthenticated_sender_login_mismatch">reject_unauthenticated_sender_login_mismatch</a></code> for additional
control over the SASL login name and the envelope sender. </p>

View File

@ -33,6 +33,8 @@ and for earlier Postfix versions that don't. </p>
<li><a href="#overload"> Symptoms of Postfix SMTP server overload </a>
<li><a href="#adapt"> Automatic stress-adaptive behavior </a>
<li><a href="#concurrency"> Service more SMTP clients at the same time </a>
<li><a href="#time"> Spend less time per SMTP client </a>
@ -41,8 +43,6 @@ and for earlier Postfix versions that don't. </p>
<li><a href="#legacy"> Temporary measures for older Postfix releases </a>
<li><a href="#adapt"> Automatic stress-adaptive behavior </a>
<li><a href="#feature"> Detecting support for stress-adaptive behavior </a>
<li><a href="#forcing"> Forcing stress-adaptive behavior on or off </a>
@ -109,6 +109,9 @@ Oct 3 20:39:27 spike postfix/master[28905]: warning: service "smtp"
Oct 3 20:39:27 spike postfix/master[28905]: warning: to avoid this
condition, increase the process count in <a href="master.5.html">master.cf</a> or reduce the
service time per client
Oct 3 20:39:27 spike postfix/master[28905]: warning: see
<a href="http://www.postfix.org/STRESS_README.html">http://www.postfix.org/STRESS_README.html</a> for examples of
stress-adapting configuration settings
</pre>
</ul>
@ -118,8 +121,116 @@ Postfix SMTP server overload is not necessarily lost. It should
still arrive once the situation returns to normal, as long as the
overload condition is temporary. </p>
<h2><a name="adapt"> Automatic stress-adaptive behavior </a></h2>
<p> Postfix version 2.5 introduces automatic stress-adaptive behavior.
It works as follows. When a "public" network service such as the
SMTP server runs into an "all server ports are busy" condition, the
Postfix <a href="master.8.html">master(8)</a> daemon logs a warning, restarts the service
(without interrupting existing network sessions), and runs the
service with "-o stress=yes" on the server process command line:
</p>
<blockquote>
<pre>
80821 ?? S 0:00.24 smtpd -n smtp -t inet -u -c -o stress=yes
</pre>
</blockquote>
<p> Normally, the Postfix <a href="master.8.html">master(8)</a> daemon runs such a service with
"-o stress=" on the command line (i.e. with an empty parameter
value): </p>
<blockquote>
<pre>
83326 ?? S 0:00.28 smtpd -n smtp -t inet -u -c -o stress=
</pre>
</blockquote>
<p> Services that have local access only never have "-o stress"
parameters on the command line. This includes services internal to
Postfix such as the queue manager, and services that listen on a
loopback interface only, such as after-filter SMTP services. </p>
<p> The "stress" parameter value is the key to making <a href="postconf.5.html">main.cf</a>
parameter settings stress adaptive. The following settings are the
default with Postfix 2.6 and later. </p>
<blockquote>
<pre>
1 <a href="postconf.5.html#smtpd_timeout">smtpd_timeout</a> = ${stress?10}${stress:300}s
2 <a href="postconf.5.html#smtpd_hard_error_limit">smtpd_hard_error_limit</a> = ${stress?1}${stress:20}
3 <a href="postconf.5.html#smtpd_junk_command_limit">smtpd_junk_command_limit</a> = ${stress?1}${stress:100}
4 # Parameters added after Postfix 2.6:
5 <a href="postconf.5.html#smtpd_per_record_deadline">smtpd_per_record_deadline</a> = ${stress?yes}${stress:no}
6 <a href="postconf.5.html#smtpd_starttls_timeout">smtpd_starttls_timeout</a> = ${stress?10}${stress:300}s
7 <a href="postconf.5.html#address_verify_poll_count">address_verify_poll_count</a> = ${stress?1}${stress:3}
</pre>
</blockquote>
<p> Translation: <p>
<ul>
<li> <p> Line 1: under conditions of stress, use an <a href="postconf.5.html#smtpd_timeout">smtpd_timeout</a>
value of 10 seconds instead of the default 300 seconds. Experience
on the postfix-users list from a variety of sysadmins shows that
reducing the "normal" <a href="postconf.5.html#smtpd_timeout">smtpd_timeout</a> to 60s is unlikely to affect
legitimate clients. However, it is unlikely to become the Postfix
default because it's not RFC compliant. Setting <a href="postconf.5.html#smtpd_timeout">smtpd_timeout</a> to
10s or even 5s under stress will still allow most
legitimate clients to connect and send mail, but may delay mail
from some clients. No mail should be lost, as long as this measure
is used only temporarily. </p>
<li> <p> Line 2: under conditions of stress, use an <a href="postconf.5.html#smtpd_hard_error_limit">smtpd_hard_error_limit</a>
of 1 instead of the default 20. This helps by disconnecting clients
after a single error, giving other clients a chance to connect.
However, this may cause significant delays with legitimate mail,
such as a mailing list that contains a few no-longer-active user
names that didn't bother to unsubscribe. No mail should be lost,
as long as this measure is used only temporarily. </p>
<li> <p> Line 3: under conditions of stress, use an
<a href="postconf.5.html#smtpd_junk_command_limit">smtpd_junk_command_limit</a> of 1 instead of the default 100. This
prevents clients from keeping connections open by repeatedly
sending HELO, EHLO, NOOP, RSET, VRFY or ETRN commands. </p>
<li> <p> Line 5: under conditions of stress, change the behavior
of <a href="postconf.5.html#smtpd_timeout">smtpd_timeout</a> and <a href="postconf.5.html#smtpd_starttls_timeout">smtpd_starttls_timeout</a>, from a time limit per
read or write system call, to a time limit to send or receive a
complete record (an SMTP command line, SMTP response line, SMTP
message content line, or TLS protocol message). </p>
<li> <p> Line 6: under conditions of stress, reduce the time limit
for TLS protocol handshake messages to 10 seconds, from the default
value of 300 seconds. See also the <a href="postconf.5.html#smtpd_timeout">smtpd_timeout</a> discussion above.
</p>
<li> <p> Line 7: under conditions of stress, do not wait up to 6
seconds for the completion of an address verification probe. If the
result is not already in the address verification cache, reply
immediately with $<a href="postconf.5.html#unverified_recipient_tempfail_action">unverified_recipient_tempfail_action</a> or
$<a href="postconf.5.html#unverified_sender_tempfail_action">unverified_sender_tempfail_action</a>. No mail should be lost, as long
as this measure is used only temporarily. </p>
</ul>
<p> The syntax of ${name?value} and ${name:value} is explained at
the beginning of the <a href="postconf.5.html">postconf(5)</a> manual page. </p>
<p> NOTE: Please keep in mind that the stress-adaptive feature is
a fairly desperate measure to keep <b>some</b> legitimate mail
flowing under overload conditions. If a site is reaching the SMTP
server process limit when there isn't an attack or bot flood
occurring, then either the process limit needs to be raised or more
hardware needs to be added. </p>
<h2><a name="concurrency"> Service more SMTP clients at the same time </a> </h2>
<p> This section and the ones that follow discuss permanent measures
against mail server overload. </p>
<p> One measure to avoid the "all server processes busy" condition
is to service more SMTP clients simultaneously. For this you need
to increase the number of Postfix SMTP server processes. This will
@ -349,95 +460,6 @@ repeatedly sending NOOP or RSET commands. </p>
as these measures are used only temporarily. The next section of
this document introduces a way to automate this process. </p>
<h2><a name="adapt"> Automatic stress-adaptive behavior </a></h2>
<p> Postfix version 2.5 introduces automatic stress-adaptive behavior.
This is also available as a source code patch for Postfix versions
2.4 and 2.3 from the mirrors listed at
<a href="http://www.postfix.org/download.html">http://www.postfix.org/download.html</a>. </p>
<p> It works as follows. When a "public" network service such as
the SMTP server runs into an "all server ports are busy" condition,
the Postfix <a href="master.8.html">master(8)</a> daemon logs a warning, restarts the service
(without interrupting existing network sessions), and runs the
service with "-o stress=yes" on the server process command line:
</p>
<blockquote>
<pre>
80821 ?? S 0:00.24 smtpd -n smtp -t inet -u -c -o stress=yes
</pre>
</blockquote>
<p> Normally, the Postfix <a href="master.8.html">master(8)</a> daemon runs such a service with
"-o stress=" on the command line (i.e. with an empty parameter
value): </p>
<blockquote>
<pre>
83326 ?? S 0:00.28 smtpd -n smtp -t inet -u -c -o stress=
</pre>
</blockquote>
<p> Services that have local access only never have "-o stress"
parameters on the command line. This includes services internal to
Postfix such as the queue manager, and services that listen on a
loopback interface only, such as after-filter SMTP services. </p>
<p> The "stress" parameter value is the key to making <a href="postconf.5.html">main.cf</a>
parameter settings stress adaptive. The following settings are the
default with Postfix 2.6 and later. With earlier Postfix versions
that have stress-adaptive support, append the lines below to the
<a href="postconf.5.html">main.cf</a> file and issue a "postfix reload" command: </p>
<blockquote>
<pre>
1 <a href="postconf.5.html#smtpd_timeout">smtpd_timeout</a> = ${stress?10}${stress:300}s
2 <a href="postconf.5.html#smtpd_hard_error_limit">smtpd_hard_error_limit</a> = ${stress?1}${stress:20}
3 <a href="postconf.5.html#smtpd_junk_command_limit">smtpd_junk_command_limit</a> = ${stress?1}${stress:100}
</pre>
</blockquote>
<p> Translation: <p>
<ul>
<li> <p> Line 1: under conditions of stress, use an <a href="postconf.5.html#smtpd_timeout">smtpd_timeout</a>
value of 10 seconds instead of the default 300 seconds. Experience
on the postfix-users list from a variety of sysadmins shows that
reducing the "normal" <a href="postconf.5.html#smtpd_timeout">smtpd_timeout</a> to 60s is unlikely to affect
legitimate clients. However, it is unlikely to become the Postfix
default because it's not RFC compliant. Setting <a href="postconf.5.html#smtpd_timeout">smtpd_timeout</a> to
10s (line 2 below) or even 5s under stress will still allow most
legitimate clients to connect and send mail, but may delay mail
from some clients. No mail should be lost, as long as this measure
is used only temporarily. </p>
<li> <p> Line 2: under conditions of stress, use an <a href="postconf.5.html#smtpd_hard_error_limit">smtpd_hard_error_limit</a>
of 1 instead of the default 20. This helps by disconnecting clients
after a single error, giving other clients a chance to connect.
However, this may cause significant delays with legitimate mail,
such as a mailing list that contains a few no-longer-active user
names that didn't bother to unsubscribe. No mail should be lost,
as long as this measure is used only temporarily. </p>
<li> <p> Line 3: under conditions of stress, use an
<a href="postconf.5.html#smtpd_junk_command_limit">smtpd_junk_command_limit</a> of 1 instead of the default 100. This
prevents clients from keeping idle connections open by repeatedly
sending NOOP or RSET commands. </p>
</ul>
<p> The syntax of ${name?value} and ${name:value} is explained at
the beginning of the <a href="postconf.5.html">postconf(5)</a> manual page. </p>
<p> NOTE: Please keep in mind that the stress-adaptive feature is
a fairly desperate measure to keep <b>some</b> legitimate mail
flowing under overload conditions. If a site is reaching the SMTP
server process limit when there isn't an attack or bot flood
occurring, then either the process limit needs to be raised or more
hardware needs to be added. </p>
<h2><a name="feature"> Detecting support for stress-adaptive behavior </a></h2>
<p> To find out if your Postfix installation supports stress-adaptive

View File

@ -10,9 +10,9 @@ LDAP_TABLE(5) LDAP_TABLE(5)
ldap_table - Postfix LDAP client configuration
<b>SYNOPSIS</b>
<b>postmap -q "</b><i>string</i><b>" <a href="ldap_table.5.html">ldap</a>:/etc/postfix/filename</b>
<b>postmap -q "<i></b>string</i><b>" <a href="ldap_table.5.html">ldap</a>:/etc/postfix/filename</b>
<b>postmap -q - <a href="ldap_table.5.html">ldap</a>:/etc/postfix/</b><i>filename</i> &lt;<i>inputfile</i>
<b>postmap -q - <a href="ldap_table.5.html">ldap</a>:/etc/postfix/<i></b>filename</i> &lt;<i>inputfile</i>
<b>DESCRIPTION</b>
The Postfix mail system uses optional tables for address
@ -716,7 +716,7 @@ LDAP_TABLE(5) LDAP_TABLE(5)
the <b>demand</b> value of <b>TLS_REQCERT</b> in LDAP client con-
figuration files.
The "try" and "never" values of <b>TLS_REQCERT</b> have no
The "try" and "allow" values of <b>TLS_REQCERT</b> have no
equivalents here. They are not available with
OpenLDAP 2.0, and in any case have questionable
security properties. Either you want TLS verified

View File

@ -3782,11 +3782,11 @@ this length; upon delivery, long lines are reconstructed. </p>
</DD>
<DT><b><a name="lmdb_map_size">lmdb_map_size</a>
(default: 10485760)</b></DT><DD>
(default: 16777216)</b></DT><DD>
<p>
The per-table size limit for programs that create OpenLDAP LMDB
tables. Specify a byte count.
The initial OpenLDAP LMDB database size limit in bytes. Each time
a database becomes full, its size limit is doubled.
</p>
<p>
@ -13683,7 +13683,8 @@ DNS lookup and increases the maximal inbound delivery rate. </p>
<DT><b><a name="smtpd_per_record_deadline">smtpd_per_record_deadline</a>
(default: normal: no, overload: yes)</b></DT><DD>
<p> Change the behavior of the <a href="postconf.5.html#smtpd_timeout">smtpd_timeout</a> time limit, from a
<p> Change the behavior of the <a href="postconf.5.html#smtpd_timeout">smtpd_timeout</a> and <a href="postconf.5.html#smtpd_starttls_timeout">smtpd_starttls_timeout</a>
time limits, from a
time limit per read or write system call, to a time limit to send
or receive a complete record (an SMTP command line, SMTP response
line, SMTP message content line, or TLS protocol message). This
@ -14706,6 +14707,12 @@ feature is available in Postfix 2.1 and later. </dd>
authenticated clients only. This feature is available in
Postfix version 2.1 and later. </dd>
<dt><b><a name="reject_known_sender_login_mismatch">reject_known_sender_login_mismatch</a></b></dt>
<dd>Apply the <a href="postconf.5.html#reject_sender_login_mismatch">reject_sender_login_mismatch</a> restriction only to MAIL
FROM addresses that are known in $<a href="postconf.5.html#smtpd_sender_login_maps">smtpd_sender_login_maps</a>. This
feature is available in Postfix version 2.11 and later. </dd>
<dt><b><a name="reject_non_fqdn_sender">reject_non_fqdn_sender</a></b></dt>
<dd>Reject the request when the MAIL FROM address is not in

View File

@ -189,7 +189,7 @@ POSTSCREEN(8) POSTSCREEN(8)
where a non-whitelisted remote SMTP client can
obtain <a href="postscreen.8.html"><b>postscreen</b>(8)</a>'s temporary whitelist status.
<b>BEFORE-GREETING TESTS</b>
<b>BEFORE 220 GREETING TESTS</b>
These tests are executed before the remote SMTP client
receives the "220 servername" greeting. If no tests remain
after the successful completion of this phase, the client
@ -253,13 +253,13 @@ POSTSCREEN(8) POSTSCREEN(8)
combined DNSBL score as defined with the
<a href="postconf.5.html#postscreen_dnsbl_sites">postscreen_dnsbl_sites</a> parameter.
<b>AFTER-GREETING TESTS</b>
<b>AFTER 220 GREETING TESTS</b>
These tests are executed after the remote SMTP client
receives the "220 servername" greeting. If a client passes
all tests during this phase, it will receive a 4XX
response to RCPT TO commands until the client hangs up.
After this, the client will be allowed to talk directly to
a Postfix SMTP server process.
response to all RCPT TO commands. After the client recon-
nects, it will be allowed to talk directly to a Postfix
SMTP server process.
<b><a href="postconf.5.html#postscreen_bare_newline_action">postscreen_bare_newline_action</a> (ignore)</b>
The action that <a href="postscreen.8.html"><b>postscreen</b>(8)</a> takes when a remote

View File

@ -658,7 +658,7 @@ version dependent behavior). The \fByes\fR setting corresponds to the
\fBdemand\fR value of \fBTLS_REQCERT\fR in LDAP client configuration
files.
.sp
The "try" and "never" values of \fBTLS_REQCERT\fR have no equivalents
The "try" and "allow" values of \fBTLS_REQCERT\fR have no equivalents
here. They are not available with OpenLDAP 2.0, and in any case have
questionable security properties. Either you want TLS verified LDAP
connections, or you don't.

View File

@ -2242,9 +2242,9 @@ This feature is available in Postfix 2.1 and later.
.SH line_length_limit (default: 2048)
Upon input, long lines are chopped up into pieces of at most
this length; upon delivery, long lines are reconstructed.
.SH lmdb_map_size (default: 10485760)
The per-table size limit for programs that create OpenLDAP LMDB
tables. Specify a byte count.
.SH lmdb_map_size (default: 16777216)
The initial OpenLDAP LMDB database size limit in bytes. Each time
a database becomes full, its size limit is doubled.
.PP
This feature is available in Postfix 2.11 and later.
.SH lmtp_address_preference (default: ipv6)
@ -9034,7 +9034,8 @@ DNS lookup and increases the maximal inbound delivery rate.
.PP
This feature is available in Postfix 2.3 and later.
.SH smtpd_per_record_deadline (default: normal: no, overload: yes)
Change the behavior of the smtpd_timeout time limit, from a
Change the behavior of the smtpd_timeout and smtpd_starttls_timeout
time limits, from a
time limit per read or write system call, to a time limit to send
or receive a complete record (an SMTP command line, SMTP response
line, SMTP message content line, or TLS protocol message). This
@ -9797,6 +9798,11 @@ Enforces the reject_sender_login_mismatch restriction for
authenticated clients only. This feature is available in
Postfix version 2.1 and later.
.br
.IP "\fBreject_known_sender_login_mismatch\fR"
Apply the reject_sender_login_mismatch restriction only to MAIL
FROM addresses that are known in $smtpd_sender_login_maps. This
feature is available in Postfix version 2.11 and later.
.br
.IP "\fBreject_non_fqdn_sender\fR"
Reject the request when the MAIL FROM address is not in
fully-qualified domain form, as required by the RFC.

View File

@ -196,7 +196,7 @@ which would introduce a common point of failure.
A list of local \fBpostscreen\fR(8) server IP addresses where a
non-whitelisted remote SMTP client can obtain \fBpostscreen\fR(8)'s temporary
whitelist status.
.SH "BEFORE-GREETING TESTS"
.SH "BEFORE 220 GREETING TESTS"
.na
.nf
.ad
@ -248,7 +248,7 @@ Available in Postfix version 2.11 and later:
Allow a remote SMTP client to skip "before" and "after 220
greeting" protocol tests, based on its combined DNSBL score as
defined with the postscreen_dnsbl_sites parameter.
.SH "AFTER-GREETING TESTS"
.SH "AFTER 220 GREETING TESTS"
.na
.nf
.ad
@ -256,9 +256,9 @@ defined with the postscreen_dnsbl_sites parameter.
These tests are executed after the remote SMTP client
receives the "220 servername" greeting. If a client passes
all tests during this phase, it will receive a 4XX response
to RCPT TO commands until the client hangs up. After this,
the client will be allowed to talk directly to a Postfix
SMTP server process.
to all RCPT TO commands. After the client reconnects, it
will be allowed to talk directly to a Postfix SMTP server
process.
.IP "\fBpostscreen_bare_newline_action (ignore)\fR"
The action that \fBpostscreen\fR(8) takes when a remote SMTP client sends
a bare newline character, that is, a newline not preceded by carriage

View File

@ -897,6 +897,7 @@ while (<>) {
s;\bcheck_sender_mx_access\b;<a href="postconf.5.html#check_sender_mx_access">$&</a>;g;
s;\bcheck_sender_ns_access\b;<a href="postconf.5.html#check_sender_ns_access">$&</a>;g;
s;\b(reject_authenti)([-</bB>]*\n*[ <bB>]*)(cated_sender_login_mismatch)\b;<a href="postconf.5.html#reject_authenticated_sender_login_mismatch">$1<\/a>$2<a href="postconf.5.html#reject_authenticated_sender_login_mismatch">$3</a>;g;
s;\breject_known_sender_login_mismatch\b;<a href="postconf.5.html#reject_known_sender_login_mismatch">$&</a>;g;
s;\breject_non_fqdn_sender\b;<a href="postconf.5.html#reject_non_fqdn_sender">$&</a>;g;
s;\breject_rhsbl_sender\b;<a href="postconf.5.html#reject_rhsbl_sender">$&</a>;g;
s;\breject_sender_login_mis[-</bB>]*\n*[ <bB>]*match\b;<a href="postconf.5.html#reject_sender_login_mismatch">$&</a>;g;

View File

@ -310,6 +310,9 @@ default</th> </tr>
<tr> <td>DEF_CONFIG_DIR</td> <td>config_directory</td>
<td>/etc/postfix</td> </tr>
<tr> <td>DEF_DB_TYPE</td> <td>default_database_type</td>
<td>hash</td> </tr>
<tr> <td>DEF_DAEMON_DIR</td> <td>daemon_directory</td>
<td>/usr/libexec/postfix</td> </tr>
@ -376,7 +379,9 @@ off Postfix features at compile time:</td> </tr>
<tr> <td> </td> <td> -DNO_DB </td> <td> Do not build with Berkeley
DB support. By default, Berkeley DB support is compiled in on
platforms that are known to support this feature. </td> </tr>
platforms that are known to support this feature. If you override
this, then you probably should also override DEF_DB_TYPE as described
in section 4.4. </td> </tr>
<tr> <td> </td> <td> -DNO_DEVPOLL </td> <td> Do not build with
Solaris <tt>/dev/poll</tt> support. By default, <tt>/dev/poll</tt>

View File

@ -19,14 +19,6 @@
<h2>Introduction</h2>
<blockquote> <p> <strong> Warning: LMDB databases have a show-stopper
bug: they do not grow beyond a specified limit, and introduce a
"database full" hard error condition that does not exist with any
other Postfix database type. This is a problem for programs whose
database grows with system load. Examples are postscreen(8),
greylisting, verify(8) and tlsmgr(8). </strong> </p> <p> <strong>
You have been warned. </strong> </p> </blockquote>
<p> Postfix uses databases of various kinds to store and look up
information. Postfix databases are specified as "type:name".
OpenLDAP LMDB implements the Postfix database type "lmdb".
@ -52,9 +44,8 @@ don't exist with other Postfix databases. </p>
<h2><a name="with_lmdb">Building Postfix with OpenLDAP LMDB support</a></h2>
<p> Postfix normally does not enable OpenLDAP LMDB support.
To build Postfix after you installed OpenLDAP LMDB from
source code, use something like: </p>
<p> Postfix normally does not enable OpenLDAP LMDB support. To
build Postfix with OpenLDAP LMDB support, use something like: </p>
<blockquote>
<pre>
@ -64,7 +55,7 @@ source code, use something like: </p>
</pre>
</blockquote>
<p> Solaris needs this: </p>
<p> Solaris may need this: </p>
<blockquote>
<pre>
@ -78,15 +69,14 @@ source code, use something like: </p>
<h2><a name="configure">Configure LMDB settings</a></h2>
<p> Postfix provides a configuration parameter that controls how
large an OpenLDAP LMDB database may grow. </p>
<p> Postfix provides one configuration parameter that controls
OpenLDAP LMDB database behavior. </p>
<ul>
<li> <p> lmdb_map_size (default: 16 MBytes per table). This setting
controls how large any OpenLDAP LMDB database may grow. It must be
set large enough to accommodate the largest table that Postfix will
use. </p>
<li> <p> lmdb_map_size (default: 16777216). This setting specifies
the initial OpenLDAP LMDB database size limit in bytes. Each time
a database becomes full, its size limit is doubled. </p>
</ul>
@ -119,15 +109,17 @@ http://highlandsun.com/hyc/mdb/. </p>
databases. </a> </h2>
<p> As documented below, conversion to LMDB introduces a number of
failure modes that don't exist with other Postfix databases. </p>
failure modes that don't exist with other Postfix databases. Some
failure modes have been eliminated on the course of time.
The writeup below reflects the status as of of LMDB 0.9.8. </p>
<!--
<p> <strong>Unexpected postmap(1)/postalias(1) "database full"
errors. </strong></p>
<dl>
<!--
<dt> Problem: </dt> <dd> <p> The "postmap lmdb:filename" command
fails with an MDB_TXN_FULL error. This problem does not exist with
other Postfix databases. </p> </dd>
@ -146,8 +138,6 @@ structures should share the same storage pool so that they can scale
with the database size, and so that all "out of storage" errors are
resolved by increasing the database size. </p> </dd>
-->
<dt> Problem: </dt> <dd> <p> The "postmap lmdb:filename" command
fails with an MDB_MAP_FULL error. This problem does not exist with
other Postfix databases. </p> </dd>
@ -177,23 +167,25 @@ availability, because the old data still exists in the database.
MDB_MAP_FULL error, it expands the database file size to the current
LMDB map size limit before terminating. </p>
<p> When the postmap(1) or postalias(1) command opens an LMDB file
larger than lmdb_map_size/3, it logs a warning and uses a larger
size limit instead: </p>
<p> Next, when you re-run the postmap(1) or postalias(1) command,
it discovers that the LMDB file is larger than lmdb_map_size/3,
logs a warning, and uses a larger LMDB map size limit instead: </p>
<p> <tt> warning: <i>filename</i>.lmdb: file size 15024128 &ge;
(lmdb map size limit 16777216)/3 -- using a larger map size limit</tt>
</p>
(lmdb map size limit 16777216)/3<br> warning: <i>filename</i>.lmdb:
using map size limit 45072384</tt> </p>
<p> The two steps above can be used to automate recovery and avoid
the need for human intervention. Just repeat "postmap lmdb:filename"
(up to some limit). After each failure it will use a 3x larger
size limit, and eventually the "database full" error will disappear.
</p>
<p> By repeating the two steps above you can automate recovery and
avoid the need for human intervention. Just repeat "postmap
lmdb:filename" (up to some limit). After each failure it will use
a 3x larger size limit, and eventually the "database full" error
should disappear. This fails only when the disk is full or when
the LMDB map size limit would exceed the memory address space size
limit. </p>
<dt> Prevention: </dt> <dd> <p> Monitor your LMDB files and make
sure that lmdb_map_size &gt; 3x the largest LMDB file size. </p>
</dd> </dl>
sure that in main.cf, lmdb_map_size &gt; 3x the largest LMDB file
size. </p> </dd> </dl>
<p> <strong>Unexpected Postfix daemon "database full" errors.
</strong></p>
@ -214,8 +206,8 @@ file larger than lmdb_map_size/3, it logs a warning and uses a
larger size limit instead: </p>
<p> <tt> warning: <i>filename</i>.lmdb: file size 15024128 &ge;
(lmdb map size limit 16777216)/3 -- using a larger map size limit</tt>
</p>
(lmdb map size limit 16777216)/3 <br>warning: <i>filename</i>.lmdb:
using map size limit 45072384</tt> </p>
<p> This can be used to automate recovery and avoid the need for
human intervention. Each time the daemon runs into a "database full"
@ -226,14 +218,16 @@ full" error will disappear, at least for a while. </p>
sure that lmdb_map_size &gt; 3x the largest LMDB file size. </p>
</dd> </dl>
-->
<p> <strong>Non-obvious recovery with postmap(1)/postalias(1)/tlsmgr(8)
from a corrupted database. </strong></p>
<dl>
<dt> Problem: </dt> <dd> <p> You cannot rebuild a corrupted LMDB
database simply by running postmap(1) or postalias(1), or by waiting
until the tlsmgr(8) daemon restarts automatically. This problem
database simply by re-running postmap(1) or postalias(1), or by
waiting until the tlsmgr(8) daemon restarts. This problem
does not exist with other Postfix databases. </p> </dd>
<dt> Background: </dt> <dd> <p> The Postfix LMDB database client
@ -245,9 +239,9 @@ That is obviously not possible with a corrupted database file. </p>
<dt> Impact: </dt> <dd> <p> Postfix does not process mail until
someone fixes the problem. </p> </dd>
<dt> Recovery: </dt> <dd> <p> First delete the ".lmdb" file by hand,
then rebuild the file with the postmap(1) or postalias(1) command,
or wait until the tlsmgr(8) daemon restarts automatically. </p>
<dt> Recovery: </dt> <dd> <p> First delete the ".lmdb" file by hand.
Then, rebuild the file with the postmap(1) or postalias(1) command,
or wait until the tlsmgr(8) daemon restarts. </p>
</dd>
<dt> Prevention: </dt> <dd>

View File

@ -1450,7 +1450,8 @@ restriction above will reject the sender address in the MAIL FROM
command if <code>smtpd_sender_login_maps</code> does not specify
the SMTP client's login name as an owner of that address. </p>
<p> See also <code>reject_authenticated_sender_login_mismatch</code> and
<p> See also <code>reject_authenticated_sender_login_mismatch</code>,
<code>reject_known_sender_login_mismatch</code>, and
<code>reject_unauthenticated_sender_login_mismatch</code> for additional
control over the SASL login name and the envelope sender. </p>

View File

@ -33,6 +33,8 @@ and for earlier Postfix versions that don't. </p>
<li><a href="#overload"> Symptoms of Postfix SMTP server overload </a>
<li><a href="#adapt"> Automatic stress-adaptive behavior </a>
<li><a href="#concurrency"> Service more SMTP clients at the same time </a>
<li><a href="#time"> Spend less time per SMTP client </a>
@ -41,8 +43,6 @@ and for earlier Postfix versions that don't. </p>
<li><a href="#legacy"> Temporary measures for older Postfix releases </a>
<li><a href="#adapt"> Automatic stress-adaptive behavior </a>
<li><a href="#feature"> Detecting support for stress-adaptive behavior </a>
<li><a href="#forcing"> Forcing stress-adaptive behavior on or off </a>
@ -109,6 +109,9 @@ Oct 3 20:39:27 spike postfix/master[28905]: warning: service "smtp"
Oct 3 20:39:27 spike postfix/master[28905]: warning: to avoid this
condition, increase the process count in master.cf or reduce the
service time per client
Oct 3 20:39:27 spike postfix/master[28905]: warning: see
<a href="http://www.postfix.org/STRESS_README.html">http://www.postfix.org/STRESS_README.html</a> for examples of
stress-adapting configuration settings
</pre>
</ul>
@ -118,8 +121,116 @@ Postfix SMTP server overload is not necessarily lost. It should
still arrive once the situation returns to normal, as long as the
overload condition is temporary. </p>
<h2><a name="adapt"> Automatic stress-adaptive behavior </a></h2>
<p> Postfix version 2.5 introduces automatic stress-adaptive behavior.
It works as follows. When a "public" network service such as the
SMTP server runs into an "all server ports are busy" condition, the
Postfix master(8) daemon logs a warning, restarts the service
(without interrupting existing network sessions), and runs the
service with "-o stress=yes" on the server process command line:
</p>
<blockquote>
<pre>
80821 ?? S 0:00.24 smtpd -n smtp -t inet -u -c -o stress=yes
</pre>
</blockquote>
<p> Normally, the Postfix master(8) daemon runs such a service with
"-o stress=" on the command line (i.e. with an empty parameter
value): </p>
<blockquote>
<pre>
83326 ?? S 0:00.28 smtpd -n smtp -t inet -u -c -o stress=
</pre>
</blockquote>
<p> Services that have local access only never have "-o stress"
parameters on the command line. This includes services internal to
Postfix such as the queue manager, and services that listen on a
loopback interface only, such as after-filter SMTP services. </p>
<p> The "stress" parameter value is the key to making main.cf
parameter settings stress adaptive. The following settings are the
default with Postfix 2.6 and later. </p>
<blockquote>
<pre>
1 smtpd_timeout = ${stress?10}${stress:300}s
2 smtpd_hard_error_limit = ${stress?1}${stress:20}
3 smtpd_junk_command_limit = ${stress?1}${stress:100}
4 # Parameters added after Postfix 2.6:
5 smtpd_per_record_deadline = ${stress?yes}${stress:no}
6 smtpd_starttls_timeout = ${stress?10}${stress:300}s
7 address_verify_poll_count = ${stress?1}${stress:3}
</pre>
</blockquote>
<p> Translation: <p>
<ul>
<li> <p> Line 1: under conditions of stress, use an smtpd_timeout
value of 10 seconds instead of the default 300 seconds. Experience
on the postfix-users list from a variety of sysadmins shows that
reducing the "normal" smtpd_timeout to 60s is unlikely to affect
legitimate clients. However, it is unlikely to become the Postfix
default because it's not RFC compliant. Setting smtpd_timeout to
10s or even 5s under stress will still allow most
legitimate clients to connect and send mail, but may delay mail
from some clients. No mail should be lost, as long as this measure
is used only temporarily. </p>
<li> <p> Line 2: under conditions of stress, use an smtpd_hard_error_limit
of 1 instead of the default 20. This helps by disconnecting clients
after a single error, giving other clients a chance to connect.
However, this may cause significant delays with legitimate mail,
such as a mailing list that contains a few no-longer-active user
names that didn't bother to unsubscribe. No mail should be lost,
as long as this measure is used only temporarily. </p>
<li> <p> Line 3: under conditions of stress, use an
smtpd_junk_command_limit of 1 instead of the default 100. This
prevents clients from keeping connections open by repeatedly
sending HELO, EHLO, NOOP, RSET, VRFY or ETRN commands. </p>
<li> <p> Line 5: under conditions of stress, change the behavior
of smtpd_timeout and smtpd_starttls_timeout, from a time limit per
read or write system call, to a time limit to send or receive a
complete record (an SMTP command line, SMTP response line, SMTP
message content line, or TLS protocol message). </p>
<li> <p> Line 6: under conditions of stress, reduce the time limit
for TLS protocol handshake messages to 10 seconds, from the default
value of 300 seconds. See also the smtpd_timeout discussion above.
</p>
<li> <p> Line 7: under conditions of stress, do not wait up to 6
seconds for the completion of an address verification probe. If the
result is not already in the address verification cache, reply
immediately with $unverified_recipient_tempfail_action or
$unverified_sender_tempfail_action. No mail should be lost, as long
as this measure is used only temporarily. </p>
</ul>
<p> The syntax of ${name?value} and ${name:value} is explained at
the beginning of the postconf(5) manual page. </p>
<p> NOTE: Please keep in mind that the stress-adaptive feature is
a fairly desperate measure to keep <b>some</b> legitimate mail
flowing under overload conditions. If a site is reaching the SMTP
server process limit when there isn't an attack or bot flood
occurring, then either the process limit needs to be raised or more
hardware needs to be added. </p>
<h2><a name="concurrency"> Service more SMTP clients at the same time </a> </h2>
<p> This section and the ones that follow discuss permanent measures
against mail server overload. </p>
<p> One measure to avoid the "all server processes busy" condition
is to service more SMTP clients simultaneously. For this you need
to increase the number of Postfix SMTP server processes. This will
@ -349,95 +460,6 @@ repeatedly sending NOOP or RSET commands. </p>
as these measures are used only temporarily. The next section of
this document introduces a way to automate this process. </p>
<h2><a name="adapt"> Automatic stress-adaptive behavior </a></h2>
<p> Postfix version 2.5 introduces automatic stress-adaptive behavior.
This is also available as a source code patch for Postfix versions
2.4 and 2.3 from the mirrors listed at
http://www.postfix.org/download.html. </p>
<p> It works as follows. When a "public" network service such as
the SMTP server runs into an "all server ports are busy" condition,
the Postfix master(8) daemon logs a warning, restarts the service
(without interrupting existing network sessions), and runs the
service with "-o stress=yes" on the server process command line:
</p>
<blockquote>
<pre>
80821 ?? S 0:00.24 smtpd -n smtp -t inet -u -c -o stress=yes
</pre>
</blockquote>
<p> Normally, the Postfix master(8) daemon runs such a service with
"-o stress=" on the command line (i.e. with an empty parameter
value): </p>
<blockquote>
<pre>
83326 ?? S 0:00.28 smtpd -n smtp -t inet -u -c -o stress=
</pre>
</blockquote>
<p> Services that have local access only never have "-o stress"
parameters on the command line. This includes services internal to
Postfix such as the queue manager, and services that listen on a
loopback interface only, such as after-filter SMTP services. </p>
<p> The "stress" parameter value is the key to making main.cf
parameter settings stress adaptive. The following settings are the
default with Postfix 2.6 and later. With earlier Postfix versions
that have stress-adaptive support, append the lines below to the
main.cf file and issue a "postfix reload" command: </p>
<blockquote>
<pre>
1 smtpd_timeout = ${stress?10}${stress:300}s
2 smtpd_hard_error_limit = ${stress?1}${stress:20}
3 smtpd_junk_command_limit = ${stress?1}${stress:100}
</pre>
</blockquote>
<p> Translation: <p>
<ul>
<li> <p> Line 1: under conditions of stress, use an smtpd_timeout
value of 10 seconds instead of the default 300 seconds. Experience
on the postfix-users list from a variety of sysadmins shows that
reducing the "normal" smtpd_timeout to 60s is unlikely to affect
legitimate clients. However, it is unlikely to become the Postfix
default because it's not RFC compliant. Setting smtpd_timeout to
10s (line 2 below) or even 5s under stress will still allow most
legitimate clients to connect and send mail, but may delay mail
from some clients. No mail should be lost, as long as this measure
is used only temporarily. </p>
<li> <p> Line 2: under conditions of stress, use an smtpd_hard_error_limit
of 1 instead of the default 20. This helps by disconnecting clients
after a single error, giving other clients a chance to connect.
However, this may cause significant delays with legitimate mail,
such as a mailing list that contains a few no-longer-active user
names that didn't bother to unsubscribe. No mail should be lost,
as long as this measure is used only temporarily. </p>
<li> <p> Line 3: under conditions of stress, use an
smtpd_junk_command_limit of 1 instead of the default 100. This
prevents clients from keeping idle connections open by repeatedly
sending NOOP or RSET commands. </p>
</ul>
<p> The syntax of ${name?value} and ${name:value} is explained at
the beginning of the postconf(5) manual page. </p>
<p> NOTE: Please keep in mind that the stress-adaptive feature is
a fairly desperate measure to keep <b>some</b> legitimate mail
flowing under overload conditions. If a site is reaching the SMTP
server process limit when there isn't an attack or bot flood
occurring, then either the process limit needs to be raised or more
hardware needs to be added. </p>
<h2><a name="feature"> Detecting support for stress-adaptive behavior </a></h2>
<p> To find out if your Postfix installation supports stress-adaptive

View File

@ -642,7 +642,7 @@
# \fBdemand\fR value of \fBTLS_REQCERT\fR in LDAP client configuration
# files.
# .sp
# The "try" and "never" values of \fBTLS_REQCERT\fR have no equivalents
# The "try" and "allow" values of \fBTLS_REQCERT\fR have no equivalents
# here. They are not available with OpenLDAP 2.0, and in any case have
# questionable security properties. Either you want TLS verified LDAP
# connections, or you don't.

View File

@ -2837,11 +2837,11 @@ The default time unit is d (days).
Specify 0 when mail delivery should be tried only once.
</p>
%PARAM lmdb_map_size 10485760
%PARAM lmdb_map_size 16777216
<p>
The per-table size limit for programs that create OpenLDAP LMDB
tables. Specify a byte count.
The initial OpenLDAP LMDB database size limit in bytes. Each time
a database becomes full, its size limit is doubled.
</p>
<p>
@ -6292,6 +6292,12 @@ feature is available in Postfix 2.1 and later. </dd>
authenticated clients only. This feature is available in
Postfix version 2.1 and later. </dd>
<dt><b><a name="reject_known_sender_login_mismatch">reject_known_sender_login_mismatch</a></b></dt>
<dd>Apply the reject_sender_login_mismatch restriction only to MAIL
FROM addresses that are known in $smtpd_sender_login_maps. This
feature is available in Postfix version 2.11 and later. </dd>
<dt><b><a name="reject_non_fqdn_sender">reject_non_fqdn_sender</a></b></dt>
<dd>Reject the request when the MAIL FROM address is not in
@ -14754,7 +14760,8 @@ service performs plaintext &lt;=&gt; TLS ciphertext conversion. <p>
%PARAM smtpd_per_record_deadline normal: no, overload: yes
<p> Change the behavior of the smtpd_timeout time limit, from a
<p> Change the behavior of the smtpd_timeout and smtpd_starttls_timeout
time limits, from a
time limit per read or write system call, to a time limit to send
or receive a complete record (an SMTP command line, SMTP response
line, SMTP message content line, or TLS protocol message). This

View File

@ -1591,6 +1591,8 @@ extern char *var_smtpd_snd_auth_maps;
#define REJECT_SENDER_LOGIN_MISMATCH "reject_sender_login_mismatch"
#define REJECT_AUTH_SENDER_LOGIN_MISMATCH \
"reject_authenticated_sender_login_mismatch"
#define REJECT_KNOWN_SENDER_LOGIN_MISMATCH \
"reject_known_sender_login_mismatch"
#define REJECT_UNAUTH_SENDER_LOGIN_MISMATCH \
"reject_unauthenticated_sender_login_mismatch"

View File

@ -20,7 +20,7 @@
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
#define MAIL_RELEASE_DATE "20130825"
#define MAIL_RELEASE_DATE "20130927"
#define MAIL_VERSION_NUMBER "2.11"
#ifdef SNAPSHOT

View File

@ -259,7 +259,7 @@
static void postalias(char *map_type, char *path_name, int postalias_flags,
int open_flags, int dict_flags)
{
VSTREAM *source_fp;
VSTREAM *NOCLOBBER source_fp;
VSTRING *line_buffer;
MKMAP *mkmap;
int lineno;
@ -286,10 +286,10 @@ static void postalias(char *map_type, char *path_name, int postalias_flags,
/* Create database. */
if (strcmp(map_type, DICT_TYPE_PROXY) == 0)
msg_fatal("can't create maps via the proxy service");
dict_flags |= DICT_FLAG_BULK_UPDATE;
if ((source_fp = vstream_fopen(path_name, O_RDONLY, 0)) == 0)
msg_fatal("open %s: %m", path_name);
}
dict_flags |= DICT_FLAG_BULK_UPDATE;
if (fstat(vstream_fileno(source_fp), &st) < 0)
msg_fatal("fstat %s: %m", path_name);
@ -321,6 +321,16 @@ static void postalias(char *map_type, char *path_name, int postalias_flags,
if ((postalias_flags & POSTALIAS_FLAG_SAVE_PERM) && S_ISREG(st.st_mode))
umask(saved_mask);
/*
* Trap "exceptions" so that we can restart a bulk-mode update after a
* recoverable error.
*/
for (;;) {
if (dict_isjmp(mkmap->dict) != 0
&& dict_setjmp(mkmap->dict) != 0
&& vstream_fseek(source_fp, SEEK_SET, 0) < 0)
msg_fatal("seek %s: %m", VSTREAM_PATH(source_fp));
/*
* Add records to the database.
*/
@ -328,16 +338,17 @@ static void postalias(char *map_type, char *path_name, int postalias_flags,
while (readlline(line_buffer, source_fp, &lineno)) {
/*
* Tokenize the input, so that we do the right thing when a quoted
* localpart contains special characters such as "@", ":" and so on.
* Tokenize the input, so that we do the right thing when a
* quoted localpart contains special characters such as "@", ":"
* and so on.
*/
if ((tok_list = tok822_scan(STR(line_buffer), (TOK822 **) 0)) == 0)
continue;
/*
* Enforce the key:value format. Disallow missing keys, multi-address
* keys, or missing values. In order to specify an empty string or
* value, enclose it in double quotes.
* Enforce the key:value format. Disallow missing keys,
* multi-address keys, or missing values. In order to specify an
* empty string or value, enclose it in double quotes.
*/
if ((colon = tok822_find_type(tok_list, ':')) == 0
|| colon->prev == 0 || colon->next == 0
@ -350,9 +361,9 @@ static void postalias(char *map_type, char *path_name, int postalias_flags,
/*
* Key must be local. XXX We should use the Postfix rewriting and
* resolving services to handle all address forms correctly. However,
* we can't count on the mail system being up when the alias database
* is being built, so we're guessing a bit.
* resolving services to handle all address forms correctly.
* However, we can't count on the mail system being up when the
* alias database is being built, so we're guessing a bit.
*/
if (tok822_rfind_type(colon, '@') || tok822_rfind_type(colon, '%')) {
msg_warn("%s, line %d: name must be local",
@ -362,12 +373,12 @@ static void postalias(char *map_type, char *path_name, int postalias_flags,
}
/*
* Split the input into key and value parts, and convert from token
* representation back to string representation. Convert the key to
* internal (unquoted) form, because the resolver produces addresses
* in internal form. Convert the value to external (quoted) form,
* because it will have to be re-parsed upon lookup. Discard the
* token representation when done.
* Split the input into key and value parts, and convert from
* token representation back to string representation. Convert
* the key to internal (unquoted) form, because the resolver
* produces addresses in internal form. Convert the value to
* external (quoted) form, because it will have to be re-parsed
* upon lookup. Discard the token representation when done.
*/
key_list = tok_list;
tok_list = 0;
@ -389,6 +400,8 @@ static void postalias(char *map_type, char *path_name, int postalias_flags,
msg_fatal("table %s:%s: write error: %m",
mkmap->dict->type, mkmap->dict->name);
}
break;
}
/*
* Update or append sendmail and NIS signatures.

View File

@ -328,7 +328,7 @@ typedef struct {
static void postmap(char *map_type, char *path_name, int postmap_flags,
int open_flags, int dict_flags)
{
VSTREAM *source_fp;
VSTREAM *NOCLOBBER source_fp;
VSTRING *line_buffer;
MKMAP *mkmap;
int lineno;
@ -349,10 +349,10 @@ static void postmap(char *map_type, char *path_name, int postmap_flags,
/* Create database. */
if (strcmp(map_type, DICT_TYPE_PROXY) == 0)
msg_fatal("can't create maps via the proxy service");
dict_flags |= DICT_FLAG_BULK_UPDATE;
if ((source_fp = vstream_fopen(path_name, O_RDONLY, 0)) == 0)
msg_fatal("open %s: %m", path_name);
}
dict_flags |= DICT_FLAG_BULK_UPDATE;
if (fstat(vstream_fileno(source_fp), &st) < 0)
msg_fatal("fstat %s: %m", path_name);
@ -384,6 +384,16 @@ static void postmap(char *map_type, char *path_name, int postmap_flags,
if ((postmap_flags & POSTMAP_FLAG_SAVE_PERM) && S_ISREG(st.st_mode))
umask(saved_mask);
/*
* Trap "exceptions" so that we can restart a bulk-mode update after a
* recoverable error.
*/
for (;;) {
if (dict_isjmp(mkmap->dict) != 0
&& dict_setjmp(mkmap->dict) != 0
&& vstream_fseek(source_fp, SEEK_SET, 0) < 0)
msg_fatal("seek %s: %m", VSTREAM_PATH(source_fp));
/*
* Add records to the database.
*/
@ -404,8 +414,8 @@ static void postmap(char *map_type, char *path_name, int postmap_flags,
trimblanks(value, 0)[0] = 0;
/*
* Enforce the "key whitespace value" format. Disallow missing keys
* or missing values.
* Enforce the "key whitespace value" format. Disallow missing
* keys or missing values.
*/
if (*key == 0 || *value == 0) {
msg_warn("%s, line %d: expected format: key whitespace value",
@ -424,6 +434,8 @@ static void postmap(char *map_type, char *path_name, int postmap_flags,
msg_fatal("table %s:%s: write error: %m",
mkmap->dict->type, mkmap->dict->name);
}
break;
}
/*
* Close the mapping database, and release the lock.

View File

@ -16,8 +16,9 @@ CFLAGS = $(DEBUG) $(OPT) $(DEFS)
TESTPROG=
PROG = postscreen
INC_DIR = ../../include
LIBS = ../../lib/libmaster.a ../../lib/libtls.a ../../lib/libglobal.a \
../../lib/libutil.a
# Fake libdns dependency, for early-binding shared-library builds.
LIBS = ../../lib/libmaster.a ../../lib/libtls.a ../../lib/libdns.a \
../../lib/libglobal.a ../../lib/libutil.a
.c.o:; $(CC) $(CFLAGS) -c $*.c

View File

@ -170,7 +170,7 @@
/* A list of local \fBpostscreen\fR(8) server IP addresses where a
/* non-whitelisted remote SMTP client can obtain \fBpostscreen\fR(8)'s temporary
/* whitelist status.
/* BEFORE-GREETING TESTS
/* BEFORE 220 GREETING TESTS
/* .ad
/* .fi
/* These tests are executed before the remote SMTP client
@ -220,15 +220,15 @@
/* Allow a remote SMTP client to skip "before" and "after 220
/* greeting" protocol tests, based on its combined DNSBL score as
/* defined with the postscreen_dnsbl_sites parameter.
/* AFTER-GREETING TESTS
/* AFTER 220 GREETING TESTS
/* .ad
/* .fi
/* These tests are executed after the remote SMTP client
/* receives the "220 servername" greeting. If a client passes
/* all tests during this phase, it will receive a 4XX response
/* to RCPT TO commands until the client hangs up. After this,
/* the client will be allowed to talk directly to a Postfix
/* SMTP server process.
/* to all RCPT TO commands. After the client reconnects, it
/* will be allowed to talk directly to a Postfix SMTP server
/* process.
/* .IP "\fBpostscreen_bare_newline_action (ignore)\fR"
/* The action that \fBpostscreen\fR(8) takes when a remote SMTP client sends
/* a bare newline character, that is, a newline not preceded by carriage

View File

@ -16,9 +16,10 @@ CFLAGS = $(DEBUG) $(OPT) $(DEFS)
TESTPROG= smtpd_token smtpd_check
PROG = smtpd
INC_DIR = ../../include
# Fake libdns dependency, for early-binding shared-library builds.
LIBS = ../../lib/libmaster.a ../../lib/libtls.a ../../lib/libdns.a \
../../lib/libxsasl.a ../../lib/libmilter.a ../../lib/libglobal.a \
../../lib/libutil.a
../../lib/libxsasl.a ../../lib/libmilter.a ../../lib/libdns.a \
../../lib/libglobal.a ../../lib/libutil.a
.c.o:; $(CC) $(CFLAGS) -c $*.c

View File

@ -3461,7 +3461,7 @@ static int reject_maps_rbl(SMTPD_STATE *state)
/* reject_auth_sender_login_mismatch - logged in client must own sender address */
static int reject_auth_sender_login_mismatch(SMTPD_STATE *state, const char *sender)
static int reject_auth_sender_login_mismatch(SMTPD_STATE *state, const char *sender, int allow_unknown_sender)
{
const RESOLVE_REPLY *reply;
const char *owners;
@ -3470,6 +3470,9 @@ static int reject_auth_sender_login_mismatch(SMTPD_STATE *state, const char *sen
char *name;
int found = 0;
#define ALLOW_UNKNOWN_SENDER 1
#define FORBID_UNKNOWN_SENDER 0
/*
* Reject if the client is logged in and does not own the sender address.
*/
@ -3487,7 +3490,8 @@ static int reject_auth_sender_login_mismatch(SMTPD_STATE *state, const char *sen
}
}
myfree(saved_owners);
}
} else if (allow_unknown_sender)
return (SMTPD_CHECK_DUNNO);
if (!found)
return (smtpd_check_reject(state, MAIL_ERROR_POLICY, 553, "5.7.1",
"<%s>: Sender address rejected: not owned by user %s",
@ -4017,7 +4021,21 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
#ifdef USE_SASL_AUTH
if (var_smtpd_sasl_enable) {
if (state->sender && *state->sender)
status = reject_auth_sender_login_mismatch(state, state->sender);
status = reject_auth_sender_login_mismatch(state,
state->sender, FORBID_UNKNOWN_SENDER);
} else
#endif
msg_warn("restriction `%s' ignored: no SASL support", name);
} else if (strcasecmp(name, REJECT_KNOWN_SENDER_LOGIN_MISMATCH) == 0) {
#ifdef USE_SASL_AUTH
if (var_smtpd_sasl_enable) {
if (state->sender && *state->sender) {
if (state->sasl_username)
status = reject_auth_sender_login_mismatch(state,
state->sender, ALLOW_UNKNOWN_SENDER);
else
status = reject_unauth_sender_login_mismatch(state, state->sender);
}
} else
#endif
msg_warn("restriction `%s' ignored: no SASL support", name);

View File

@ -8,8 +8,9 @@ CFLAGS = $(DEBUG) $(OPT) $(DEFS)
TESTPROG=
PROG = tlsmgr
INC_DIR = ../../include
LIBS = ../../lib/libmaster.a ../../lib/libtls.a ../../lib/libglobal.a \
../../lib/libutil.a
# Fake libdns dependency, for early-binding shared-library builds.
LIBS = ../../lib/libmaster.a ../../lib/libtls.a ../../lib/libdns.a \
../../lib/libglobal.a ../../lib/libutil.a
.c.o:; $(CC) $(CFLAGS) -c $*.c

View File

@ -1833,6 +1833,8 @@ stream_trigger.o: sys_defs.h
stream_trigger.o: trigger.h
sys_compat.o: sys_compat.c
sys_compat.o: sys_defs.h
timecmp.o: timecmp.c
timecmp.o: timecmp.h
timed_connect.o: iostuff.h
timed_connect.o: msg.h
timed_connect.o: sane_connect.h

View File

@ -15,6 +15,13 @@
* System library.
*/
#include <fcntl.h>
#include <setjmp.h>
#ifdef NO_SIGSETJMP
#define DICT_JMP_BUF jmp_buf
#else
#define DICT_JMP_BUF sigjmp_buf
#endif
/*
* Utility library.
@ -56,6 +63,7 @@ typedef struct DICT {
VSTRING *fold_buf; /* key folding buffer */
DICT_OWNER owner; /* provenance */
int error; /* last operation only */
DICT_JMP_BUF *jbuf; /* exception handling */
} DICT;
extern DICT *dict_alloc(const char *, const char *, ssize_t);
@ -206,6 +214,31 @@ extern DICT *dict_surrogate(const char *, const char *, int, int, const char *,.
*/
#define DICT_TYPE_NOFILE "non-existent"
/*
* Duplicated from vstream(3). This should probably be abstracted out.
*
* Exception handling. We use pointer to jmp_buf to avoid a lot of unused
* baggage for streams that don't need this functionality.
*
* XXX sigsetjmp()/siglongjmp() save and restore the signal mask which can
* avoid surprises in code that manipulates signals, but unfortunately some
* systems have bugs in their implementation.
*/
#ifdef NO_SIGSETJMP
#define dict_setjmp(stream) setjmp((stream)->jbuf[0])
#define dict_longjmp(stream, val) longjmp((stream)->jbuf[0], (val))
#else
#define dict_setjmp(stream) sigsetjmp((stream)->jbuf[0], 1)
#define dict_longjmp(stream, val) siglongjmp((stream)->jbuf[0], (val))
#endif
#define dict_isjmp(stream) ((stream)->jbuf != 0)
/*
* Temporary API. If exception handling proves to be useful,
* dict_jmp_alloc() should be integrated into dict_alloc().
*/
extern void dict_jmp_alloc(DICT *);
/* LICENSE
/* .ad
/* .fi

View File

@ -13,6 +13,9 @@
/*
/* void dict_free(dict)
/* DICT *ptr;
/*
/* void dict_jmp_alloc(dict)
/* DICT *ptr;
/* DESCRIPTION
/* dict_alloc() allocates memory for a dictionary structure of
/* \fIsize\fR bytes, initializes all generic dictionary
@ -35,6 +38,9 @@
/* It is up to the caller to dispose of any memory that was allocated
/* by the caller.
/*
/* dict_jmp_alloc() implements preliminary support for exception
/* handling. This will eventually be built into dict_alloc().
/*
/* Arguments:
/* .IP dict_type
/* The official name for this type of dictionary, as used by
@ -145,6 +151,7 @@ DICT *dict_alloc(const char *dict_type, const char *dict_name, ssize_t size)
dict->owner.status = DICT_OWNER_UNKNOWN;
dict->owner.uid = ~0;
dict->error = DICT_ERR_NONE;
dict->jbuf = 0;
return dict;
}
@ -154,5 +161,20 @@ void dict_free(DICT *dict)
{
myfree(dict->type);
myfree(dict->name);
if (dict->jbuf)
myfree((char *) dict->jbuf);
myfree((char *) dict);
}
/*
* TODO: add a dict_flags() argument to dict_alloc() and handle jump buffer
* allocation there.
*/
/* dict_jmp_alloc - enable exception handling */
void dict_jmp_alloc(DICT *dict)
{
if (dict->jbuf == 0)
dict->jbuf = (DICT_JMP_BUF *) mymalloc(sizeof(DICT_JMP_BUF));
}

View File

@ -14,27 +14,20 @@
/* int open_flags;
/* int dict_flags;
/* DESCRIPTION
/* dict_lmdb_open() opens the named LMDB database and makes it available
/* via the generic interface described in dict_open(3).
/* dict_lmdb_open() opens the named LMDB database and makes
/* it available via the generic interface described in
/* dict_open(3).
/*
/* The dict_lmdb_map_size variable specifies a non-default
/* per-table memory map size. The map size is also the maximum
/* size the table can grow to, so it must be set large enough
/* to accomodate the largest tables in use.
/*
/* As a safety measure, when Postfix opens an LMDB database
/* it will set the memory map size to at least 3x the ".lmdb"
/* file size, so that there is room for the file to grow. This
/* ensures that a process can recover from a "table full" error
/* with a simple terminate-and-restart.
/*
/* As a second safety measure, when an update or delete operation
/* runs into an MDB_MAP_FULL error, Postfix will extend the
/* database file to the current ".lmdb" file size, so that the
/* above workaround will be triggered the next time the database
/* is opened.
/* The dict_lmdb_map_size variable specifies the initial
/* database memory map size. When a map becomes full its size
/* is doubled, and other programs pick up the size change.
/* DIAGNOSTICS
/* Fatal errors: cannot open file, file write error, out of memory.
/* Fatal errors: cannot open file, file write error, out of
/* memory.
/* BUGS
/* The on-the-fly map resize operations require no concurrent
/* activity in the same database by other threads in the same
/* process.
/* SEE ALSO
/* dict(3) generic dictionary manager
/* LICENSE
@ -44,9 +37,14 @@
/* AUTHOR(S)
/* Howard Chu
/* Symas Corporation
/*
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*--*/
#include "sys_defs.h"
#include <sys_defs.h>
#ifdef HAS_LMDB
@ -61,20 +59,29 @@
#include PATH_LMDB_H
#else
#include <lmdb.h>
#endif
/*
* As of LMDB 0.9.8 the database size limit can be updated on-the-fly. The
* only limit that remains is imposed by the hardware address space. Earlier
* LMDB versions are not suitable for use with Postfix.
*/
#if MDB_VERSION_FULL < MDB_VERINT(0, 9, 8)
#error "Build with LMDB version 0.9.8 or later"
#endif
/* Utility library. */
#include "msg.h"
#include "mymalloc.h"
#include "htable.h"
#include "iostuff.h"
#include "vstring.h"
#include "myflock.h"
#include "stringops.h"
#include "dict.h"
#include "dict_lmdb.h"
#include "warn_stat.h"
#include <msg.h>
#include <mymalloc.h>
#include <htable.h>
#include <iostuff.h>
#include <vstring.h>
#include <myflock.h>
#include <stringops.h>
#include <dict.h>
#include <dict_lmdb.h>
#include <warn_stat.h>
/* Application-specific. */
@ -84,55 +91,423 @@ typedef struct {
MDB_dbi dbi; /* database handle */
MDB_txn *txn; /* bulk update transaction */
MDB_cursor *cursor; /* for sequence ops */
size_t map_size; /* per-database size limit */
VSTRING *key_buf; /* key buffer */
VSTRING *val_buf; /* result buffer */
VSTRING *val_buf; /* value buffer */
/* The following facilitate LMDB quirk workarounds. */
int dict_api_retries; /* workarounds per dict(3) call */
int bulk_mode_retries; /* workarounds per bulk transaction */
int open_flags; /* dict(3) open flags */
int env_flags; /* LMDB open flags */
} DICT_LMDB;
/*
* The LMDB database filename suffix happens to equal our DICT_TYPE_LMDB
* prefix, but that doesn't mean it is kosher to use DICT_TYPE_LMDB where a
* suffix is needed, so we define an explicit suffix here.
*/
#define DICT_LMDB_SUFFIX "lmdb"
/*
* Make a safe string copy that is guaranteed to be null-terminated.
*/
#define SCOPY(buf, data, size) \
vstring_str(vstring_strncpy(buf ? buf : (buf = vstring_alloc(10)), data, size))
size_t dict_lmdb_map_size = (10 * 1024 * 1024); /* 10MB default mmap
* size */
/*
* Postfix writers recover from a "map full" error by increasing the memory
* map size with a factor DICT_LMDB_SIZE_INCR (up to some limit) and
* retrying the transaction.
*
* Each dict(3) API call is retried no more than a few times. For bulk-mode
* transactions the number of retries is proportional to the size of the
* address space.
*/
#ifndef SSIZE_T_MAX /* The maximum map size */
#define SSIZE_T_MAX __MAXINT__(ssize_t) /* XXX Assumes two's complement */
#endif
#define DICT_LMDB_SIZE_INCR 2 /* Increase size by 1 bit on retry */
#define DICT_LMDB_SIZE_MAX SSIZE_T_MAX
#define DICT_LMDB_API_RETRY_LIMIT 2 /* Retries per dict(3) API call */
#define DICT_LMDB_BULK_RETRY_LIMIT \
(2 * sizeof(size_t) * CHAR_BIT) /* Retries per bulk-mode transaction */
/*
* XXX Should dict_lmdb_max_readers be configurable? Is this a per-database
* property? Per-process? Does it need to be the same for all processes?
*/
size_t dict_lmdb_map_size = 8192; /* Minimum size without SIGSEGV */
unsigned int dict_lmdb_max_readers = 216; /* 200 postfix processes,
* plus some extra */
/* #define msg_verbose 1 */
/*
* Out-of-space safety net. When an update or delete operation fails with
* MDB_MAP_FULL, extend the database file size so that the next
* dict_lmdb_open() call will force a 3x over-allocation.
* The purpose of the error-recovering functions below is to hide LMDB
* quirks (MAP_FULL, MAP_CHANGED), so that the dict(3) API routines can
* pretend that those quirks don't exist, and focus on their own job.
*
* XXX This strategy assumes that a bogus file size will not affect LMDB
* operation. In private communication on August 18, 2013, Howard Chu
* confirmed this as follows: "It will have no effect. LMDB internally
* accounts for the last used page#, the filesystem's notion of filesize
* isn't used for any purpose."
* - To recover from a single-transaction LMDB error, each wrapper function
* uses tail recursion instead of goto. Since LMDB errors are rare, code
* clarity is more important than speed.
*
* We make no assumptions about which LMDB operations may fail with
* MDB_MAP_FULL. Instead we wrap all LMDB operations inside a Postfix
* function that may change a database.
* - To recover from a bulk-mode transaction LMDB error, the error-recovery
* code jumps back into the caller to some pre-arranged point (the closest
* thing that C has to exception handling). With postmap, this means that
* bulk-mode LMDB error recovery is limited to input that is seekable.
*/
#define DICT_LMDB_WRAPPER(dict, status, operation) \
((status = operation) == MDB_MAP_FULL ? \
(dict_lmdb_grow(dict), status) : status)
/* dict_lmdb_grow - grow the DB file if the last txn failed to grow it */
/* dict_lmdb_prepare - LMDB-specific (re)initialization before actual access */
static void dict_lmdb_grow(DICT *dict)
static void dict_lmdb_prepare(DICT_LMDB *dict_lmdb)
{
struct stat st;
char *mdb_path = concatenate(dict->name, "." DICT_TYPE_LMDB, (char *) 0);
int status;
/*
* After MDB_MAP_FULL error, expand the file size to trigger the 3x size
* limit workaround on the next open() attempt.
* This is called before accessing the database, or after recovery from
* an LMDB error. dict_lmdb->txn is either the database open()
* transaction or a freshly-created bulk-mode transaction.
*
* - With O_TRUNC we make a "drop" request before populating the database.
*
* - With DICT_FLAG_BULK_UPDATE we commit a bulk-mode transaction when the
* database is closed.
*/
if (stat(mdb_path, &st) == 0 && st.st_size < dict_lmdb_map_size
&& truncate(mdb_path, dict_lmdb_map_size) < 0)
msg_warn("dict_lmdb_grow: cannot grow database file %s:%s: %m",
dict->type, dict->name);
myfree(mdb_path);
if (dict_lmdb->open_flags & O_TRUNC) {
if ((status = mdb_drop(dict_lmdb->txn, dict_lmdb->dbi, 0)) != 0)
msg_fatal("truncate %s:%s: %s",
dict_lmdb->dict.type, dict_lmdb->dict.name,
mdb_strerror(status));
if ((dict_lmdb->dict.flags & DICT_FLAG_BULK_UPDATE) == 0) {
if ((status = mdb_txn_commit(dict_lmdb->txn)))
msg_fatal("truncate %s:%s: %s",
dict_lmdb->dict.type, dict_lmdb->dict.name,
mdb_strerror(status));
dict_lmdb->txn = NULL;
}
} else if ((dict_lmdb->env_flags & MDB_RDONLY) != 0
|| (dict_lmdb->dict.flags & DICT_FLAG_BULK_UPDATE) == 0) {
mdb_txn_abort(dict_lmdb->txn);
dict_lmdb->txn = NULL;
}
}
/* dict_lmdb_recover - recover from LMDB errors */
static int dict_lmdb_recover(DICT_LMDB *dict_lmdb, int status)
{
const char *myname = "dict_lmdb_recover";
MDB_envinfo info;
/*
* Limit the number of recovery attempts per dict(3) API request.
*/
if ((dict_lmdb->dict_api_retries += 1) > DICT_LMDB_API_RETRY_LIMIT) {
if (msg_verbose)
msg_info("%s: %s:%s too many recovery attempts %d",
myname, dict_lmdb->dict.type, dict_lmdb->dict.name,
dict_lmdb->dict_api_retries);
return (status);
}
/*
* If we can recover from the error, we clear the error condition and the
* caller should retry the failed operation immediately. Otherwise, the
* caller should terminate with a fatal run-time error and the program
* should be re-run later.
*
* dict_lmdb->txn is either null (non-bulk transaction error), or an aborted
* bulk-mode transaction. If we want to make this wrapper layer suitable
* for general use, then the bulk/non-bulk distinction should be made
* less specific to Postfix.
*/
switch (status) {
/*
* As of LMDB 0.9.8 when a non-bulk update runs into a "map full"
* error, we can resize the environment's memory map and clear the
* error condition. The caller should retry immediately.
*/
case MDB_MAP_FULL:
/* Can we increase the memory map? Give up if we can't. */
if (dict_lmdb->map_size < DICT_LMDB_SIZE_MAX / DICT_LMDB_SIZE_INCR) {
dict_lmdb->map_size = dict_lmdb->map_size * DICT_LMDB_SIZE_INCR;
} else if (dict_lmdb->map_size < DICT_LMDB_SIZE_MAX) {
dict_lmdb->map_size = DICT_LMDB_SIZE_MAX;
} else {
/* Sorry, but we are already maxed out. */
break;
}
/* Resize the memory map. */
if (msg_verbose)
msg_info("updating database %s:%s size limit to %lu",
dict_lmdb->dict.type, dict_lmdb->dict.name,
(unsigned long) dict_lmdb->map_size);
if ((status = mdb_env_set_mapsize(dict_lmdb->env,
dict_lmdb->map_size)) != 0)
msg_fatal("env_set_mapsize %s:%s to %lu: %s",
dict_lmdb->dict.type, dict_lmdb->dict.name,
(unsigned long) dict_lmdb->map_size,
mdb_strerror(status));
break;
/*
* When a writer resizes the database, read-only applications must
* increase their LMDB memory map size limit, too. Otherwise, they
* won't be able to read a table after it grows.
*
* As of LMDB 0.9.8 we can import the new memory map size limit into the
* database environment by calling mdb_env_set_mapsize() with a zero
* size argument. Then we extract the map size limit for later use.
* The caller should retry immediately.
*/
case MDB_MAP_RESIZED:
if ((status = mdb_env_set_mapsize(dict_lmdb->env, 0)) != 0)
msg_fatal("env_set_mapsize %s:%s to 0: %s",
dict_lmdb->dict.type, dict_lmdb->dict.name,
mdb_strerror(status));
/* Do not panic. Maps may shrink after bulk update. */
mdb_env_info(dict_lmdb->env, &info);
dict_lmdb->map_size = info.me_mapsize;
if (msg_verbose)
msg_info("importing database %s:%s new size limit %lu",
dict_lmdb->dict.type, dict_lmdb->dict.name,
(unsigned long) dict_lmdb->map_size);
break;
/*
* We can't solve this problem. The application should terminate with
* a fatal run-time error and the program should be re-run later.
*/
default:
break;
}
/*
* If a bulk-mode transaction error is recoverable, build a new bulk-mode
* transaction from scratch, by making a long jump back into the caller
* at some pre-arranged point.
*/
if (dict_lmdb->txn != 0 && status == 0
&& (dict_lmdb->bulk_mode_retries += 1) <= DICT_LMDB_BULK_RETRY_LIMIT) {
status = mdb_txn_begin(dict_lmdb->env, NULL,
dict_lmdb->env_flags & MDB_RDONLY,
&dict_lmdb->txn);
if (status != 0)
msg_fatal("txn_begin %s:%s: %s",
dict_lmdb->dict.type, dict_lmdb->dict.name,
mdb_strerror(status));
dict_lmdb_prepare(dict_lmdb);
dict_longjmp(&dict_lmdb->dict, 1);
}
return (status);
}
/* dict_lmdb_txn_begin - mdb_txn_begin() wrapper with LMDB error recovery */
static void dict_lmdb_txn_begin(DICT_LMDB *dict_lmdb, int rdonly, MDB_txn **txn)
{
int status;
if ((status = mdb_txn_begin(dict_lmdb->env, NULL, rdonly, txn)) != 0) {
if ((status = dict_lmdb_recover(dict_lmdb, status)) == 0) {
dict_lmdb_txn_begin(dict_lmdb, rdonly, txn);
return;
}
msg_fatal("%s:%s: error starting %s transaction: %s",
dict_lmdb->dict.type, dict_lmdb->dict.name,
rdonly ? "read" : "write", mdb_strerror(status));
}
}
/* dict_lmdb_get - mdb_get() wrapper with LMDB error recovery */
static int dict_lmdb_get(DICT_LMDB *dict_lmdb, MDB_val *mdb_key,
MDB_val *mdb_value)
{
MDB_txn *txn;
int status;
/*
* Start a read transaction if there's no bulk-mode txn.
*/
if (dict_lmdb->txn)
txn = dict_lmdb->txn;
else
dict_lmdb_txn_begin(dict_lmdb, MDB_RDONLY, &txn);
/*
* Do the lookup.
*/
if ((status = mdb_get(txn, dict_lmdb->dbi, mdb_key, mdb_value)) != 0
&& status != MDB_NOTFOUND) {
mdb_txn_abort(txn);
if (dict_lmdb->txn == 0)
txn = 0;
if ((status = dict_lmdb_recover(dict_lmdb, status)) == 0)
return (dict_lmdb_get(dict_lmdb, mdb_key, mdb_value));
}
/*
* Close the read txn if it's not the bulk-mode txn.
*/
if (txn && dict_lmdb->txn == 0)
mdb_txn_abort(txn);
return (status);
}
/* dict_lmdb_put - mdb_put() wrapper with LMDB error recovery */
static int dict_lmdb_put(DICT_LMDB *dict_lmdb, MDB_val *mdb_key,
MDB_val *mdb_value, int flags)
{
MDB_txn *txn;
int status;
/*
* Start a write transaction if there's no bulk-mode txn.
*/
if (dict_lmdb->txn)
txn = dict_lmdb->txn;
else
dict_lmdb_txn_begin(dict_lmdb, 0, &txn);
/*
* Do the update.
*/
if ((status = mdb_put(txn, dict_lmdb->dbi, mdb_key, mdb_value, flags)) != 0
&& status != MDB_KEYEXIST) {
mdb_txn_abort(txn);
if (dict_lmdb->txn == 0)
txn = 0;
if ((status = dict_lmdb_recover(dict_lmdb, status)) == 0)
return (dict_lmdb_put(dict_lmdb, mdb_key, mdb_value, flags));
}
/*
* Commit the transaction if it's not the bulk-mode txn.
*/
if (txn && dict_lmdb->txn == 0 && (status = mdb_txn_commit(txn)) != 0) {
if ((status = dict_lmdb_recover(dict_lmdb, status)) == 0)
return (dict_lmdb_put(dict_lmdb, mdb_key, mdb_value, flags));
msg_fatal("error committing database %s:%s: %s",
dict_lmdb->dict.type, dict_lmdb->dict.name,
mdb_strerror(status));
}
return (status);
}
/* dict_lmdb_del - mdb_del() wrapper with LMDB error recovery */
static int dict_lmdb_del(DICT_LMDB *dict_lmdb, MDB_val *mdb_key)
{
MDB_txn *txn;
int status;
/*
* Start a write transaction if there's no bulk-mode txn.
*/
if (dict_lmdb->txn)
txn = dict_lmdb->txn;
else
dict_lmdb_txn_begin(dict_lmdb, 0, &txn);
/*
* Do the update.
*/
if ((status = mdb_del(txn, dict_lmdb->dbi, mdb_key, NULL)) != 0
&& status != MDB_NOTFOUND) {
mdb_txn_abort(txn);
if (dict_lmdb->txn == 0)
txn = 0;
if ((status = dict_lmdb_recover(dict_lmdb, status)) == 0)
return (dict_lmdb_del(dict_lmdb, mdb_key));
}
/*
* Commit the transaction if it's not the bulk-mode txn.
*/
if (txn && dict_lmdb->txn == 0 && (status = mdb_txn_commit(txn)) != 0) {
if ((status = dict_lmdb_recover(dict_lmdb, status)) == 0)
return (dict_lmdb_del(dict_lmdb, mdb_key));
msg_fatal("error committing database %s:%s: %s",
dict_lmdb->dict.type, dict_lmdb->dict.name,
mdb_strerror(status));
}
return (status);
}
/* dict_lmdb_cursor_get - mdb_cursor_get() wrapper with LMDB error recovery */
static int dict_lmdb_cursor_get(DICT_LMDB *dict_lmdb, MDB_val *mdb_key,
MDB_val *mdb_value, MDB_cursor_op op)
{
MDB_txn *txn;
int status;
/*
* Open a read transaction and cursor if needed.
*/
if (dict_lmdb->cursor == 0) {
dict_lmdb_txn_begin(dict_lmdb, MDB_RDONLY, &txn);
if ((status = mdb_cursor_open(txn, dict_lmdb->dbi, &dict_lmdb->cursor))) {
if ((status = dict_lmdb_recover(dict_lmdb, status)) == 0)
return (dict_lmdb_cursor_get(dict_lmdb, mdb_key, mdb_value, op));
msg_fatal("%s:%s: cursor_open database: %s",
dict_lmdb->dict.type, dict_lmdb->dict.name,
mdb_strerror(status));
}
}
/*
* Database lookup.
*/
status = mdb_cursor_get(dict_lmdb->cursor, mdb_key, mdb_value, op);
if (status != 0 && status != MDB_NOTFOUND)
if ((status = dict_lmdb_recover(dict_lmdb, status)) == 0)
return (dict_lmdb_cursor_get(dict_lmdb, mdb_key, mdb_value, op));
return (status);
}
/* dict_lmdb_finish - wrapper with LMDB error recovery */
static void dict_lmdb_finish(DICT_LMDB *dict_lmdb)
{
int status;
/*
* Finish the bulk-mode transaction. If dict_lmdb_recover() returns after
* a bulk-mode transaction error, then it was unable to recover.
*/
if (dict_lmdb->txn) {
if ((status = mdb_txn_commit(dict_lmdb->txn)) != 0) {
(void) dict_lmdb_recover(dict_lmdb, status);
msg_fatal("%s:%s: closing dictionary: %s",
dict_lmdb->dict.type, dict_lmdb->dict.name,
mdb_strerror(status));
}
}
/*
* Clean up after an unfinished sequence() operation.
*/
if (dict_lmdb->cursor) {
MDB_txn *txn = mdb_cursor_txn(dict_lmdb->cursor);
mdb_cursor_close(dict_lmdb->cursor);
mdb_txn_abort(txn);
}
}
/*
* With all recovery from LMDB quirks encapsulated in the routines above,
* the dict(3) API routines below can pretend that LMDB quirks don't exist
* and focus on their own job: accessing or updating the database.
*/
/* dict_lmdb_lookup - find database entry */
static const char *dict_lmdb_lookup(DICT *dict, const char *name)
@ -140,11 +515,11 @@ static const char *dict_lmdb_lookup(DICT *dict, const char *name)
DICT_LMDB *dict_lmdb = (DICT_LMDB *) dict;
MDB_val mdb_key;
MDB_val mdb_value;
MDB_txn *txn;
const char *result = 0;
int status, klen;
dict->error = 0;
dict_lmdb->dict_api_retries = 0;
klen = strlen(name);
/*
@ -163,14 +538,6 @@ static const char *dict_lmdb_lookup(DICT *dict, const char *name)
name = lowercase(vstring_str(dict->fold_buf));
}
/*
* Start a read transaction if there's no global txn.
*/
if (dict_lmdb->txn)
txn = dict_lmdb->txn;
else if ((status = mdb_txn_begin(dict_lmdb->env, NULL, MDB_RDONLY, &txn)))
msg_fatal("%s: txn_begin(read) dictionary: %s", dict_lmdb->dict.name, mdb_strerror(status));
/*
* See if this LMDB file was written with one null byte appended to key
* and value.
@ -178,10 +545,15 @@ static const char *dict_lmdb_lookup(DICT *dict, const char *name)
if (dict->flags & DICT_FLAG_TRY1NULL) {
mdb_key.mv_data = (void *) name;
mdb_key.mv_size = klen + 1;
status = mdb_get(txn, dict_lmdb->dbi, &mdb_key, &mdb_value);
if (!status) {
status = dict_lmdb_get(dict_lmdb, &mdb_key, &mdb_value);
if (status == 0) {
dict->flags &= ~DICT_FLAG_TRY0NULL;
result = SCOPY(dict_lmdb->val_buf, mdb_value.mv_data, mdb_value.mv_size);
result = SCOPY(dict_lmdb->val_buf, mdb_value.mv_data,
mdb_value.mv_size);
} else if (status != MDB_NOTFOUND) {
msg_fatal("error reading %s:%s: %s",
dict_lmdb->dict.type, dict_lmdb->dict.name,
mdb_strerror(status));
}
}
@ -192,19 +564,17 @@ static const char *dict_lmdb_lookup(DICT *dict, const char *name)
if (result == 0 && (dict->flags & DICT_FLAG_TRY0NULL)) {
mdb_key.mv_data = (void *) name;
mdb_key.mv_size = klen;
status = mdb_get(txn, dict_lmdb->dbi, &mdb_key, &mdb_value);
if (!status) {
status = dict_lmdb_get(dict_lmdb, &mdb_key, &mdb_value);
if (status == 0) {
dict->flags &= ~DICT_FLAG_TRY1NULL;
result = SCOPY(dict_lmdb->val_buf, mdb_value.mv_data, mdb_value.mv_size);
result = SCOPY(dict_lmdb->val_buf, mdb_value.mv_data,
mdb_value.mv_size);
} else if (status != MDB_NOTFOUND) {
msg_fatal("error reading %s:%s: %s",
dict_lmdb->dict.type, dict_lmdb->dict.name,
mdb_strerror(status));
}
}
/*
* Close the read txn if it's not the global txn.
*/
if (!dict_lmdb->txn)
mdb_txn_abort(txn);
return (result);
}
@ -215,10 +585,10 @@ static int dict_lmdb_update(DICT *dict, const char *name, const char *value)
DICT_LMDB *dict_lmdb = (DICT_LMDB *) dict;
MDB_val mdb_key;
MDB_val mdb_value;
MDB_txn *txn;
int status;
dict->error = 0;
dict_lmdb->dict_api_retries = 0;
/*
* Sanity check.
@ -236,6 +606,7 @@ static int dict_lmdb_update(DICT *dict, const char *name, const char *value)
name = lowercase(vstring_str(dict->fold_buf));
}
mdb_key.mv_data = (void *) name;
mdb_value.mv_data = (void *) value;
mdb_key.mv_size = strlen(name);
mdb_value.mv_size = strlen(value);
@ -261,40 +632,27 @@ static int dict_lmdb_update(DICT *dict, const char *name, const char *value)
mdb_value.mv_size++;
}
/*
* Start a write transaction if there's no global txn.
*/
if (dict_lmdb->txn)
txn = dict_lmdb->txn;
else if (DICT_LMDB_WRAPPER(dict, status,
mdb_txn_begin(dict_lmdb->env, NULL, 0, &txn)))
msg_fatal("%s: txn_begin(write) dictionary: %s", dict_lmdb->dict.name, mdb_strerror(status));
/*
* Do the update.
*/
(void) DICT_LMDB_WRAPPER(dict, status,
mdb_put(txn, dict_lmdb->dbi, &mdb_key, &mdb_value,
(dict->flags & DICT_FLAG_DUP_REPLACE) ? 0 : MDB_NOOVERWRITE));
if (status) {
status = dict_lmdb_put(dict_lmdb, &mdb_key, &mdb_value,
(dict->flags & DICT_FLAG_DUP_REPLACE) ? 0 : MDB_NOOVERWRITE);
if (status != 0) {
if (status == MDB_KEYEXIST) {
if (dict->flags & DICT_FLAG_DUP_IGNORE)
/* void */ ;
else if (dict->flags & DICT_FLAG_DUP_WARN)
msg_warn("%s: duplicate entry: \"%s\"", dict_lmdb->dict.name, name);
msg_warn("%s:%s: duplicate entry: \"%s\"",
dict_lmdb->dict.type, dict_lmdb->dict.name, name);
else
msg_fatal("%s: duplicate entry: \"%s\"", dict_lmdb->dict.name, name);
msg_fatal("%s:%s: duplicate entry: \"%s\"",
dict_lmdb->dict.type, dict_lmdb->dict.name, name);
} else {
msg_fatal("error writing LMDB database %s: %s", dict_lmdb->dict.name, mdb_strerror(status));
msg_fatal("error updating %s:%s: %s",
dict_lmdb->dict.type, dict_lmdb->dict.name,
mdb_strerror(status));
}
}
/*
* Commit the transaction if it's not the global txn.
*/
if (!dict_lmdb->txn && DICT_LMDB_WRAPPER(dict, status, mdb_txn_commit(txn)))
msg_fatal("error committing LMDB database %s: %s", dict_lmdb->dict.name, mdb_strerror(status));
return (status);
}
@ -304,10 +662,10 @@ static int dict_lmdb_delete(DICT *dict, const char *name)
{
DICT_LMDB *dict_lmdb = (DICT_LMDB *) dict;
MDB_val mdb_key;
MDB_txn *txn;
int status = 1, klen, rc;
int status = 1, klen;
dict->error = 0;
dict_lmdb->dict_api_retries = 0;
klen = strlen(name);
/*
@ -326,15 +684,6 @@ static int dict_lmdb_delete(DICT *dict, const char *name)
name = lowercase(vstring_str(dict->fold_buf));
}
/*
* Start a write transaction if there's no global txn.
*/
if (dict_lmdb->txn)
txn = dict_lmdb->txn;
else if (DICT_LMDB_WRAPPER(dict, status,
mdb_txn_begin(dict_lmdb->env, NULL, 0, &txn)))
msg_fatal("%s: txn_begin(write) dictionary: %s", dict_lmdb->dict.name, mdb_strerror(status));
/*
* See if this LMDB file was written with one null byte appended to key
* and value.
@ -342,13 +691,14 @@ static int dict_lmdb_delete(DICT *dict, const char *name)
if (dict->flags & DICT_FLAG_TRY1NULL) {
mdb_key.mv_data = (void *) name;
mdb_key.mv_size = klen + 1;
(void) DICT_LMDB_WRAPPER(dict, status,
mdb_del(txn, dict_lmdb->dbi, &mdb_key, NULL));
if (status) {
status = dict_lmdb_del(dict_lmdb, &mdb_key);
if (status != 0) {
if (status == MDB_NOTFOUND)
status = 1;
else
msg_fatal("error deleting from %s: %s", dict_lmdb->dict.name, mdb_strerror(status));
msg_fatal("error deleting from %s:%s: %s",
dict_lmdb->dict.type, dict_lmdb->dict.name,
mdb_strerror(status));
} else {
dict->flags &= ~DICT_FLAG_TRY0NULL; /* found */
}
@ -361,28 +711,22 @@ static int dict_lmdb_delete(DICT *dict, const char *name)
if (status > 0 && (dict->flags & DICT_FLAG_TRY0NULL)) {
mdb_key.mv_data = (void *) name;
mdb_key.mv_size = klen;
(void) DICT_LMDB_WRAPPER(dict, status,
mdb_del(txn, dict_lmdb->dbi, &mdb_key, NULL));
if (status) {
status = dict_lmdb_del(dict_lmdb, &mdb_key);
if (status != 0) {
if (status == MDB_NOTFOUND)
status = 1;
else
msg_fatal("error deleting from %s: %s", dict_lmdb->dict.name, mdb_strerror(status));
msg_fatal("error deleting from %s:%s: %s",
dict_lmdb->dict.type, dict_lmdb->dict.name,
mdb_strerror(status));
} else {
dict->flags &= ~DICT_FLAG_TRY1NULL; /* found */
}
}
/*
* Commit the transaction if it's not the global txn.
*/
if (!dict_lmdb->txn && DICT_LMDB_WRAPPER(dict, rc, mdb_txn_commit(txn)))
msg_fatal("error committing LMDB database %s: %s", dict_lmdb->dict.name, mdb_strerror(rc));
return (status);
}
/* traverse the dictionary */
/* dict_lmdb_sequence - traverse the dictionary */
static int dict_lmdb_sequence(DICT *dict, int function,
const char **key, const char **value)
@ -396,6 +740,7 @@ static int dict_lmdb_sequence(DICT *dict, int function,
int status;
dict->error = 0;
dict_lmdb->dict_api_retries = 0;
/*
* Determine the seek function.
@ -411,50 +756,42 @@ static int dict_lmdb_sequence(DICT *dict, int function,
msg_panic("%s: invalid function: %d", myname, function);
}
/*
* Open a read transaction and cursor if needed.
*/
if (dict_lmdb->cursor == 0) {
if ((status = mdb_txn_begin(dict_lmdb->env, NULL, MDB_RDONLY, &txn)))
msg_fatal("%s: txn_begin(read) dictionary: %s", dict_lmdb->dict.name, mdb_strerror(status));
if ((status = mdb_cursor_open(txn, dict_lmdb->dbi, &dict_lmdb->cursor)))
msg_fatal("%s: cursor_open dictionary: %s", dict_lmdb->dict.name, mdb_strerror(status));
}
/*
* Database lookup.
*/
status = mdb_cursor_get(dict_lmdb->cursor, &mdb_key, &mdb_value, op);
if (status && status != MDB_NOTFOUND)
msg_fatal("%s: seeking dictionary: %s", dict_lmdb->dict.name, mdb_strerror(status));
status = dict_lmdb_cursor_get(dict_lmdb, &mdb_key, &mdb_value, op);
if (status == MDB_NOTFOUND) {
switch (status) {
/*
* Caller must read to end, to ensure cursor gets closed.
* Copy the key and value so they are guaranteed null terminated.
*/
case 0:
*key = SCOPY(dict_lmdb->key_buf, mdb_key.mv_data, mdb_key.mv_size);
if (mdb_value.mv_data != 0 && mdb_value.mv_size > 0)
*value = SCOPY(dict_lmdb->val_buf, mdb_value.mv_data,
mdb_value.mv_size);
break;
/*
* Destroy cursor and read transaction.
*/
case MDB_NOTFOUND:
status = 1;
txn = mdb_cursor_txn(dict_lmdb->cursor);
mdb_cursor_close(dict_lmdb->cursor);
mdb_txn_abort(txn);
dict_lmdb->cursor = 0;
} else {
break;
/*
* Copy the key so that it is guaranteed null terminated.
* Bust.
*/
*key = SCOPY(dict_lmdb->key_buf, mdb_key.mv_data, mdb_key.mv_size);
if (mdb_value.mv_data != 0 && mdb_value.mv_size > 0) {
/*
* Copy the value so that it is guaranteed null terminated.
*/
*value = SCOPY(dict_lmdb->val_buf, mdb_value.mv_data, mdb_value.mv_size);
status = 0;
default:
msg_fatal("error seeking %s:%s: %s",
dict_lmdb->dict.type, dict_lmdb->dict.name,
mdb_strerror(status));
}
}
return (status);
}
@ -472,20 +809,8 @@ static void dict_lmdb_close(DICT *dict)
{
DICT_LMDB *dict_lmdb = (DICT_LMDB *) dict;
if (dict_lmdb->txn) {
int status;
(void) DICT_LMDB_WRAPPER(dict, status, mdb_txn_commit(dict_lmdb->txn));
if (status)
msg_fatal("%s: closing dictionary: %s", dict_lmdb->dict.name, mdb_strerror(status));
dict_lmdb->cursor = NULL;
}
if (dict_lmdb->cursor) {
MDB_txn *txn = mdb_cursor_txn(dict_lmdb->cursor);
mdb_cursor_close(dict_lmdb->cursor);
mdb_txn_abort(txn);
}
dict_lmdb->dict_api_retries = 0;
dict_lmdb_finish(dict_lmdb);
if (dict_lmdb->dict.stat_fd >= 0)
close(dict_lmdb->dict.stat_fd);
mdb_env_close(dict_lmdb->env);
@ -509,6 +834,7 @@ DICT *dict_lmdb_open(const char *path, int open_flags, int dict_flags)
MDB_dbi dbi;
char *mdb_path;
int env_flags, status;
size_t map_size = dict_lmdb_map_size;
mdb_path = concatenate(path, "." DICT_TYPE_LMDB, (char *) 0);
@ -519,73 +845,33 @@ DICT *dict_lmdb_open(const char *path, int open_flags, int dict_flags)
if ((status = mdb_env_create(&env)))
msg_fatal("env_create %s: %s", mdb_path, mdb_strerror(status));
/*
* Try to ensure that the LMDB size limit is at least 3x the current LMDB
* file size. This ensures that Postfix daemon processes can recover from
* a "table full" error with a simple terminate-and-restart.
*
* Note: read-only applications must increase their LMDB size limit, too,
* otherwise they won't be able to read a table after it grows.
*/
#ifndef SIZE_T_MAX
#define SIZE_T_MAX __MAXINT__(size_t)
#endif
#define LMDB_SIZE_FUDGE_FACTOR 3
if (stat(mdb_path, &st) == 0
&& st.st_size >= dict_lmdb_map_size / LMDB_SIZE_FUDGE_FACTOR) {
msg_warn("%s: file size %lu >= (%s map size limit %ld)/%d -- "
"using a larger map size limit",
mdb_path, (unsigned long) st.st_size,
DICT_TYPE_LMDB, (long) dict_lmdb_map_size,
LMDB_SIZE_FUDGE_FACTOR);
/* Defense against naive optimizers. */
if (st.st_size < SIZE_T_MAX / LMDB_SIZE_FUDGE_FACTOR)
dict_lmdb_map_size = st.st_size * LMDB_SIZE_FUDGE_FACTOR;
else
dict_lmdb_map_size = SIZE_T_MAX;
}
if ((status = mdb_env_set_mapsize(env, dict_lmdb_map_size)))
if (stat(mdb_path, &st) == 0 && st.st_size > map_size)
map_size = st.st_size;
if ((status = mdb_env_set_mapsize(env, map_size)))
msg_fatal("env_set_mapsize %s: %s", mdb_path, mdb_strerror(status));
if ((status = mdb_env_set_maxreaders(env, dict_lmdb_max_readers)))
msg_fatal("env_set_maxreaders %s: %s", mdb_path, mdb_strerror(status));
if ((status = mdb_env_open(env, mdb_path, env_flags, 0644)))
msg_fatal("env_open %s: %s", mdb_path, mdb_strerror(status));
/*
* Gracefully handle the most common mistake.
*/
if ((status = mdb_env_open(env, mdb_path, env_flags, 0644))) {
mdb_env_close(env);
return (dict_surrogate(DICT_TYPE_LMDB, path, open_flags, dict_flags,
"open database %s: %m", mdb_path));
}
if ((status = mdb_txn_begin(env, NULL, env_flags & MDB_RDONLY, &txn)))
msg_fatal("txn_begin %s: %s", mdb_path, mdb_strerror(status));
/*
* mdb_open requires a txn, but since the default DB always exists in an
* LMDB environment, we usually don't need to do anything else with the
* txn.
* mdb_open() requires a txn, but since the default DB always exists in
* an LMDB environment, we usually don't need to do anything else with
* the txn. It is currently used for bulk transactions.
*/
if ((status = mdb_open(txn, NULL, 0, &dbi)))
msg_fatal("mdb_open %s: %s", mdb_path, mdb_strerror(status));
/*
* Cases where we use the mdb_open transaction:
*
* - With O_TRUNC we make the "drop" request before populating the database.
*
* - With DICT_FLAG_BULK_UPDATE we commit the transaction when the database
* is closed.
*/
if (open_flags & O_TRUNC) {
if ((status = mdb_drop(txn, dbi, 0)))
msg_fatal("truncate %s: %s", mdb_path, mdb_strerror(status));
if ((dict_flags & DICT_FLAG_BULK_UPDATE) == 0) {
if ((status = mdb_txn_commit(txn)))
msg_fatal("truncate %s: %s", mdb_path, mdb_strerror(status));
txn = NULL;
}
} else if ((env_flags & MDB_RDONLY) != 0
|| (dict_flags & DICT_FLAG_BULK_UPDATE) == 0) {
mdb_txn_abort(txn);
txn = NULL;
}
dict_lmdb = (DICT_LMDB *) dict_alloc(DICT_TYPE_LMDB, path, sizeof(*dict_lmdb));
dict_lmdb->dict.lookup = dict_lmdb_lookup;
dict_lmdb->dict.update = dict_lmdb_update;
@ -619,14 +905,22 @@ DICT *dict_lmdb_open(const char *path, int open_flags, int dict_flags)
dict_lmdb->dict.fold_buf = vstring_alloc(10);
dict_lmdb->env = env;
dict_lmdb->dbi = dbi;
/* Save the write txn if we opened with O_TRUNC */
dict_lmdb->txn = txn;
dict_lmdb->map_size = map_size;
dict_lmdb->cursor = 0;
dict_lmdb->key_buf = 0;
dict_lmdb->val_buf = 0;
/* The following facilitate transparent error recovery. */
dict_lmdb->dict_api_retries = 0;
dict_lmdb->bulk_mode_retries = 0;
dict_lmdb->open_flags = open_flags;
dict_lmdb->env_flags = env_flags;
dict_lmdb->txn = txn;
dict_lmdb_prepare(dict_lmdb);
if (dict_flags & DICT_FLAG_BULK_UPDATE)
dict_jmp_alloc(&dict_lmdb->dict); /* build into dict_alloc() */
myfree(mdb_path);
return (DICT_DEBUG (&dict_lmdb->dict));

View File

@ -44,6 +44,16 @@
/* DICT *(*open) (const char *, int, int);
/*
/* ARGV *dict_mapnames()
/*
/* int dict_isjmp(dict)
/* DICT *dict;
/*
/* int dict_setjmp(dict)
/* DICT *dict;
/*
/* int dict_longjmp(dict, val)
/* DICT *dict;
/* int val;
/* DESCRIPTION
/* This module implements a low-level interface to multiple
/* physical dictionary types.
@ -112,6 +122,10 @@
/* .IP DICT_FLAG_PARANOID
/* A combination of all the paranoia flags: DICT_FLAG_NO_REGSUB,
/* DICT_FLAG_NO_PROXY and DICT_FLAG_NO_UNAUTH.
/* .IP DICT_FLAG_BULK_UPDATE
/* Enable preliminary code for bulk-mode database updates.
/* The caller must create an exception handler with dict_jmp_alloc()
/* and must trap exceptions from the database client with dict_setjmp().
/* .IP DICT_FLAG_DEBUG
/* Enable additional logging.
/* .PP
@ -169,6 +183,18 @@
/*
/* dict_mapnames() returns a sorted list with the names of all available
/* dictionary types.
/*
/* dict_setjmp() saves processing context and makes that context
/* available for use with dict_longjmp(). Normally, dict_setjmp()
/* returns zero. A non-zero result means that dict_setjmp()
/* returned through a dict_longjmp() call; the result is the
/* \fIval\fR argment given to dict_longjmp(). dict_isjmp()
/* returns non-zero when dict_setjmp() and dict_longjmp()
/* are enabled for a given dictionary.
/*
/* NB: non-local jumps such as dict_longjmp() are not safe for
/* jumping out of any routine that manipulates DICT data.
/* longjmp() like calls are best avoided in signal handlers.
/* DIAGNOSTICS
/* Fatal error: open error, unsupported dictionary type, attempt to
/* update non-writable dictionary.

View File

@ -45,6 +45,8 @@ void dict_test(int argc, char **argv)
int n;
int rc;
#define USAGE "verbose|del key|get key|put key=value|first|next|masks|flags"
signal(SIGPIPE, SIG_IGN);
msg_vstream_init(argv[0], VSTREAM_ERR);
@ -88,7 +90,7 @@ void dict_test(int argc, char **argv)
if (*bufp == '#')
continue;
if ((cmd = mystrtok(&bufp, " ")) == 0) {
vstream_printf("usage: verbose|del key|get key|put key=value|first|next|masks|flags\n");
vstream_printf("usage: %s\n", USAGE);
vstream_fflush(VSTREAM_OUT);
continue;
}
@ -143,7 +145,7 @@ void dict_test(int argc, char **argv)
vstream_printf("DICT_FLAG_INST_MASK %s\n",
dict_flags_str(DICT_FLAG_INST_MASK));
} else {
vstream_printf("usage: del key|get key|put key=value|first|next|masks|flags\n");
vstream_printf("usage: %s\n", USAGE);
}
vstream_fflush(VSTREAM_OUT);
}

View File

@ -46,15 +46,15 @@
#define HAS_FSYNC
#define HAS_DB
#define HAS_SA_LEN
#define DEF_DB_TYPE "hash"
#define NATIVE_DB_TYPE "hash"
#if (defined(__NetBSD_Version__) && __NetBSD_Version__ >= 104250000)
#define ALIAS_DB_MAP "hash:/etc/mail/aliases" /* sendmail 8.10 */
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/mail/aliases" /* sendmail 8.10 */
#endif
#if (defined(OpenBSD) && OpenBSD >= 200006)
#define ALIAS_DB_MAP "hash:/etc/mail/aliases" /* OpenBSD 2.7 */
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/mail/aliases" /* OpenBSD 2.7 */
#endif
#ifndef ALIAS_DB_MAP
#define ALIAS_DB_MAP "hash:/etc/aliases"
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/aliases"
#endif
#define GETTIMEOFDAY(t) gettimeofday(t,(struct timezone *) 0)
#define ROOT_PATH "/bin:/usr/bin:/sbin:/usr/sbin"
@ -227,8 +227,8 @@
#define HAS_FSYNC
#define HAS_DB
#define HAS_SA_LEN
#define DEF_DB_TYPE "hash"
#define ALIAS_DB_MAP "hash:/etc/aliases"
#define NATIVE_DB_TYPE "hash"
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/aliases"
#define GETTIMEOFDAY(t) gettimeofday(t,(struct timezone *) 0)
#define ROOT_PATH "/bin:/usr/bin:/sbin:/usr/sbin"
#define USE_STATFS
@ -287,12 +287,12 @@
#define HAS_FSYNC
/* might be set by makedef */
#ifdef HAS_DB
#define DEF_DB_TYPE "hash"
#define ALIAS_DB_MAP "hash:/etc/aliases"
#define NATIVE_DB_TYPE "hash"
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/aliases"
#else
#define HAS_DBM
#define DEF_DB_TYPE "dbm"
#define ALIAS_DB_MAP "dbm:/etc/aliases"
#define NATIVE_DB_TYPE "dbm"
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/aliases"
#endif
extern int optind;
extern char *optarg;
@ -336,8 +336,8 @@ extern int h_errno;
#define HAS_FSYNC
#define HAVE_BASENAME
#define HAS_DBM
#define DEF_DB_TYPE "dbm"
#define ALIAS_DB_MAP "dbm:/var/adm/sendmail/aliases"
#define NATIVE_DB_TYPE "dbm"
#define ALIAS_DB_MAP DEF_DB_TYPE ":/var/adm/sendmail/aliases"
extern int optind; /* XXX use <getopt.h> */
extern char *optarg; /* XXX use <getopt.h> */
extern int opterr; /* XXX use <getopt.h> */
@ -383,8 +383,8 @@ extern int opterr; /* XXX use <getopt.h> */
#define DEF_MAILBOX_LOCK "flock, dotlock"
#define HAS_FSYNC
#define HAS_DBM
#define DEF_DB_TYPE "dbm"
#define ALIAS_DB_MAP "dbm:/etc/aliases"
#define NATIVE_DB_TYPE "dbm"
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/aliases"
extern int optind;
extern char *optarg;
extern int opterr;
@ -429,8 +429,8 @@ extern int opterr;
#define DEF_MAILBOX_LOCK "fcntl, dotlock"
#define HAS_FSYNC
#define HAS_DBM
#define DEF_DB_TYPE "dbm"
#define ALIAS_DB_MAP "dbm:/etc/mail/aliases"
#define NATIVE_DB_TYPE "dbm"
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/mail/aliases"
#ifndef NO_NIS
#define HAS_NIS
#define HAS_NISPLUS
@ -506,8 +506,8 @@ extern int opterr;
#define DEF_MAILBOX_LOCK "fcntl, dotlock"
#define HAS_FSYNC
#define HAS_DBM
#define DEF_DB_TYPE "dbm"
#define ALIAS_DB_MAP "dbm:/etc/mail/aliases"
#define NATIVE_DB_TYPE "dbm"
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/mail/aliases"
#ifndef NO_NIS
#define HAS_NIS
#endif
@ -537,8 +537,8 @@ extern int opterr;
#define DEF_MAILBOX_LOCK "fcntl, dotlock"
#define HAS_FSYNC
#define HAS_DBM
#define DEF_DB_TYPE "dbm"
#define ALIAS_DB_MAP "dbm:/etc/mail/aliases"
#define NATIVE_DB_TYPE "dbm"
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/mail/aliases"
#ifndef NO_NIS
#define HAS_NIS */
#endif
@ -580,8 +580,8 @@ extern int opterr;
#define USE_SYS_SELECT_H
#define HAS_FSYNC
#define HAS_DBM
#define DEF_DB_TYPE "dbm"
#define ALIAS_DB_MAP "dbm:/etc/aliases"
#define NATIVE_DB_TYPE "dbm"
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/aliases"
#ifndef NO_NIS
#define HAS_NIS
#endif
@ -634,8 +634,8 @@ extern int opterr;
#define USE_SYS_SELECT_H
#define HAS_FSYNC
#define HAS_DBM
#define DEF_DB_TYPE "dbm"
#define ALIAS_DB_MAP "dbm:/etc/aliases"
#define NATIVE_DB_TYPE "dbm"
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/aliases"
#ifndef NO_NIS
#define HAS_NIS
#endif
@ -680,8 +680,8 @@ extern int initgroups(const char *, int);
#define USE_SYS_SELECT_H
#define HAS_FSYNC
#define HAS_DBM
#define DEF_DB_TYPE "dbm"
#define ALIAS_DB_MAP "dbm:/etc/aliases"
#define NATIVE_DB_TYPE "dbm"
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/aliases"
#ifndef NO_NIS
#define HAS_NIS
#endif
@ -722,8 +722,8 @@ extern int initgroups(const char *, int);
#define DEF_MAILBOX_LOCK "fcntl, dotlock"
#define HAS_FSYNC
#define HAS_DBM
#define DEF_DB_TYPE "dbm"
#define ALIAS_DB_MAP "dbm:/etc/aliases"
#define NATIVE_DB_TYPE "dbm"
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/aliases"
#ifndef NO_NIS
#define HAS_NIS
#endif
@ -766,8 +766,8 @@ extern int initgroups(const char *, int);
#define DEF_MAILBOX_LOCK "fcntl, dotlock" /* RedHat >= 4.x */
#define HAS_FSYNC
#define HAS_DB
#define DEF_DB_TYPE "hash"
#define ALIAS_DB_MAP "hash:/etc/aliases"
#define NATIVE_DB_TYPE "hash"
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/aliases"
#ifndef NO_NIS
#define HAS_NIS
#endif
@ -843,8 +843,8 @@ extern int initgroups(const char *, int);
#define DEF_MAILBOX_LOCK "dotlock" /* verified RedHat 3.03 */
#define HAS_FSYNC
#define HAS_DB
#define DEF_DB_TYPE "hash"
#define ALIAS_DB_MAP "hash:/etc/aliases"
#define NATIVE_DB_TYPE "hash"
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/aliases"
#ifndef NO_NIS
#define HAS_NIS
#endif
@ -877,8 +877,8 @@ extern int initgroups(const char *, int);
#define DEF_MAILBOX_LOCK "fcntl, dotlock" /* RedHat >= 4.x */
#define HAS_FSYNC
#define HAS_DB
#define DEF_DB_TYPE "hash"
#define ALIAS_DB_MAP "hash:/etc/aliases"
#define NATIVE_DB_TYPE "hash"
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/aliases"
#ifndef NO_NIS
#define HAS_NIS
#endif
@ -943,8 +943,8 @@ extern int initgroups(const char *, int);
#define INTERNAL_LOCK MYFLOCK_STYLE_FCNTL
#define DEF_MAILBOX_LOCK "fcntl, dotlock"
#define HAS_FSYNC
#define DEF_DB_TYPE "dbm"
#define ALIAS_DB_MAP "dbm:/etc/mail/aliases"
#define NATIVE_DB_TYPE "dbm"
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/mail/aliases"
#define ROOT_PATH "/usr/bin:/sbin:/usr/sbin"
#define MISSING_SETENV
#ifndef NO_NIS
@ -982,8 +982,8 @@ extern int h_errno; /* <netdb.h> imports too much stuff */
#define INTERNAL_LOCK MYFLOCK_STYLE_FCNTL
#define DEF_MAILBOX_LOCK "fcntl, dotlock"
#define HAS_FSYNC
#define DEF_DB_TYPE "dbm"
#define ALIAS_DB_MAP "dbm:/etc/mail/aliases"
#define NATIVE_DB_TYPE "dbm"
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/mail/aliases"
#define ROOT_PATH "/usr/bin:/sbin:/usr/sbin"
#define MISSING_SETENV
#ifndef NO_NIS
@ -1027,8 +1027,8 @@ extern int h_errno; /* <netdb.h> imports too much stuff */
#define MISSING_SETENV
#define MISSING_RLIMIT_FSIZE
#define GETTIMEOFDAY(t) gettimeofday(t,(struct timezone *) 0)
#define DEF_DB_TYPE "dbm"
#define ALIAS_DB_MAP "dbm:/usr/lib/aliases"
#define NATIVE_DB_TYPE "dbm"
#define ALIAS_DB_MAP DEF_DB_TYPE ":/usr/lib/aliases"
#define ROOT_PATH "/bin:/usr/bin:/etc"
#define _PATH_BSHELL "/bin/sh"
#define _PATH_MAILDIR "/usr/mail"
@ -1087,7 +1087,7 @@ extern int h_errno;
#define _PATH_DEFPATH "/bin:/usr/bin:/usr/ucb"
#define _PATH_STDPATH "/bin:/usr/bin:/usr/ucb"
#define ROOT_PATH "/bin:/usr/bin:/usr/etc:/usr/ucb"
#define DEF_DB_TYPE "dbm"
#define NATIVE_DB_TYPE "dbm"
#define ALIAS_DB_MAP "netinfo:/aliases"
#include <libc.h>
#define MISSING_POSIX_S_IS
@ -1142,7 +1142,7 @@ typedef unsigned short mode_t;
#define _PATH_DEFPATH "/bin:/usr/bin:/usr/ucb"
#define _PATH_STDPATH "/bin:/usr/bin:/usr/ucb"
#define ROOT_PATH "/bin:/usr/bin:/usr/etc:/usr/ucb"
#define DEF_DB_TYPE "dbm"
#define NATIVE_DB_TYPE "dbm"
#define ALIAS_DB_MAP "netinfo:/aliases"
#include <libc.h>
#define MISSING_POSIX_S_IS
@ -1179,8 +1179,8 @@ typedef unsigned short mode_t;
#define FIONREAD_IN_SYS_FILIO_H
#define USE_SYS_SOCKIO_H
#define HAS_DBM
#define DEF_DB_TYPE "dbm"
#define ALIAS_DB_MAP "dbm:/var/adm/sendmail/aliases"
#define NATIVE_DB_TYPE "dbm"
#define ALIAS_DB_MAP DEF_DB_TYPE ":/var/adm/sendmail/aliases"
extern int optind; /* XXX use <getopt.h> */
extern char *optarg; /* XXX use <getopt.h> */
extern int opterr; /* XXX use <getopt.h> */
@ -1209,8 +1209,8 @@ extern int opterr; /* XXX use <getopt.h> */
#define INTERNAL_LOCK MYFLOCK_STYLE_FCNTL
#define DEF_MAILBOX_LOCK "fcntl, dotlock"
#define HAS_FSYNC
#define DEF_DB_TYPE "hash"
#define ALIAS_DB_MAP "hash:/etc/aliases"
#define NATIVE_DB_TYPE "hash"
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/aliases"
/* Uncomment the following line if you have NIS package installed */
/* #define HAS_NIS */
#define USE_SYS_SOCKIO_H
@ -1242,8 +1242,8 @@ extern int h_errno;
#define DEF_MAILBOX_LOCK "fcntl, dotlock"
#define HAS_FSYNC
#define HAS_DBM
#define DEF_DB_TYPE "dbm"
#define ALIAS_DB_MAP "dbm:/etc/mail/aliases"
#define NATIVE_DB_TYPE "dbm"
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/mail/aliases"
#define DBM_NO_TRAILING_NULL
#ifndef NO_NIS
#define HAS_NIS
@ -1303,6 +1303,10 @@ extern int h_errno;
#endif
#endif
#ifndef DEF_DB_TYPE
#define DEF_DB_TYPE NATIVE_DB_TYPE
#endif
#define CAST_CHAR_PTR_TO_INT(cptr) ((int) (long) (cptr))
#define CAST_INT_TO_CHAR_PTR(ival) ((char *) (long) (ival))