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:
parent
26eddc2f78
commit
2bf7cc9990
8
postfix/.indent.pro
vendored
8
postfix/.indent.pro
vendored
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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 |
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
====================================
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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>
|
||||
|
@ -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 ≥
|
||||
(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> > 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> > 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 ≥
|
||||
(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> > 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>
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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
|
||||
|
@ -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> <<i>inputfile</i>
|
||||
<b>postmap -q - <a href="ldap_table.5.html">ldap</a>:/etc/postfix/<i></b>filename</i> <<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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
@ -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 ≥
|
||||
(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 > 3x the largest LMDB file size. </p>
|
||||
</dd> </dl>
|
||||
sure that in main.cf, lmdb_map_size > 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 ≥
|
||||
(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 > 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>
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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 <=> 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
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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))
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user