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
|
-TABOUNCE
|
||||||
-TADDR_MATCH_LIST
|
-TADDR_MATCH_LIST
|
||||||
-TADDR_PATTERN
|
-TADDR_PATTERN
|
||||||
@ -119,8 +120,8 @@
|
|||||||
-TDSN_BUF
|
-TDSN_BUF
|
||||||
-TDSN_SPLIT
|
-TDSN_SPLIT
|
||||||
-TDSN_STAT
|
-TDSN_STAT
|
||||||
-TEC_KEY
|
|
||||||
-TEC_GROUP
|
-TEC_GROUP
|
||||||
|
-TEC_KEY
|
||||||
-TEDIT_FILE
|
-TEDIT_FILE
|
||||||
-TEVENT_MASK
|
-TEVENT_MASK
|
||||||
-TEVP_PKEY
|
-TEVP_PKEY
|
||||||
@ -176,6 +177,7 @@
|
|||||||
-TMATCH_OPS
|
-TMATCH_OPS
|
||||||
-TMBLOCK
|
-TMBLOCK
|
||||||
-TMBOX
|
-TMBOX
|
||||||
|
-TMDB_txn
|
||||||
-TMILTER
|
-TMILTER
|
||||||
-TMILTER8
|
-TMILTER8
|
||||||
-TMILTERS
|
-TMILTERS
|
||||||
@ -330,10 +332,10 @@
|
|||||||
-TWATCHDOG
|
-TWATCHDOG
|
||||||
-TWATCH_FD
|
-TWATCH_FD
|
||||||
-TX509
|
-TX509
|
||||||
|
-TX509V3_CTX
|
||||||
-TX509_EXTENSION
|
-TX509_EXTENSION
|
||||||
-TX509_NAME
|
-TX509_NAME
|
||||||
-TX509_STORE_CTX
|
-TX509_STORE_CTX
|
||||||
-TX509V3_CTX
|
|
||||||
-TXSASL_CLIENT
|
-TXSASL_CLIENT
|
||||||
-TXSASL_CLIENT_CREATE_ARGS
|
-TXSASL_CLIENT_CREATE_ARGS
|
||||||
-TXSASL_CLIENT_IMPL
|
-TXSASL_CLIENT_IMPL
|
||||||
@ -358,9 +360,9 @@
|
|||||||
-Tsasl_secret_t
|
-Tsasl_secret_t
|
||||||
-Tsfsistat
|
-Tsfsistat
|
||||||
-Tsize_t
|
-Tsize_t
|
||||||
|
-Tssize_t
|
||||||
-Tssl_cipher_stack_t
|
-Tssl_cipher_stack_t
|
||||||
-Tssl_comp_stack_t
|
-Tssl_comp_stack_t
|
||||||
-Tssize_t
|
|
||||||
-Ttime_t
|
-Ttime_t
|
||||||
-Tx509_extension_stack_t
|
-Tx509_extension_stack_t
|
||||||
-Tx509_stack_t
|
-Tx509_stack_t
|
||||||
|
@ -18719,10 +18719,11 @@ Apologies for any names omitted.
|
|||||||
|
|
||||||
20130615
|
20130615
|
||||||
|
|
||||||
Interoperability: turn on SHA-2XX digests by force. This
|
TLS Interoperability: turn on SHA-2 digests by force. This
|
||||||
improves interoperability with clients and servers with
|
improves interoperability with clients and servers that
|
||||||
ancient OpenSSL versions and that that prematurely deploy
|
deploy SHA-2 digests without the required support for
|
||||||
SHA-2 certificates. Viktor Dukhovni. File: tls/tls_misc.c
|
TLSv1.2-style digest negotiation. Based on patch by Viktor
|
||||||
|
Dukhovni. Files: tls/tls_client.c, tls/tls_server.c.
|
||||||
|
|
||||||
20130616
|
20130616
|
||||||
|
|
||||||
@ -18912,3 +18913,33 @@ Apologies for any names omitted.
|
|||||||
src/smtp/smtp_addr.h, src/smtp/smtp_connect.c,
|
src/smtp/smtp_addr.h, src/smtp/smtp_connect.c,
|
||||||
src/smtp/smtp_params.c, src/smtp/smtp_tls_policy.c,
|
src/smtp/smtp_params.c, src/smtp/smtp_tls_policy.c,
|
||||||
src/tls/tls.h, src/tls/tls_dane.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:
|
Parameters whose defaults can be specified in this way are:
|
||||||
|
|
||||||
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
|
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
|
||||||
|MMaaccrroo nnaammee |ddeeffaauulltt vvaalluuee ffoorr|ttyyppiiccaall ddeeffaauulltt |
|
|MMaaccrroo nnaammee |ddeeffaauulltt vvaalluuee ffoorr |ttyyppiiccaall ddeeffaauulltt |
|
||||||
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|
||||||
|DEF_COMMAND_DIR |command_directory|/usr/sbin |
|
|DEF_COMMAND_DIR |command_directory |/usr/sbin |
|
||||||
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|
||||||
|DEF_CONFIG_DIR |config_directory |/etc/postfix |
|
|DEF_CONFIG_DIR |config_directory |/etc/postfix |
|
||||||
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|
||||||
|DEF_DAEMON_DIR |daemon_directory |/usr/libexec/postfix|
|
|DEF_DB_TYPE |default_database_type|hash |
|
||||||
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|
||||||
|DEF_DATA_DIR |data_directory |/var/lib/postfix |
|
|DEF_DAEMON_DIR |daemon_directory |/usr/libexec/postfix|
|
||||||
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|
||||||
|DEF_MAILQ_PATH |mailq_path |/usr/bin/mailq |
|
|DEF_DATA_DIR |data_directory |/var/lib/postfix |
|
||||||
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|
||||||
|DEF_HTML_DIR |html_directory |no |
|
|DEF_MAILQ_PATH |mailq_path |/usr/bin/mailq |
|
||||||
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|
||||||
|DEF_MANPAGE_DIR |manpage_directory|/usr/local/man |
|
|DEF_HTML_DIR |html_directory |no |
|
||||||
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|
||||||
|DEF_NEWALIAS_PATH|newaliases_path |/usr/bin/newaliases |
|
|DEF_MANPAGE_DIR |manpage_directory |/usr/local/man |
|
||||||
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|
||||||
|DEF_QUEUE_DIR |queue_directory |/var/spool/postfix |
|
|DEF_NEWALIAS_PATH|newaliases_path |/usr/bin/newaliases |
|
||||||
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|
||||||
|DEF_README_DIR |readme_directory |no |
|
|DEF_QUEUE_DIR |queue_directory |/var/spool/postfix |
|
||||||
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|
||||||
|DEF_SENDMAIL_PATH|sendmail_path |/usr/sbin/sendmail |
|
|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
|
Note: the data_directory parameter (for caches and pseudo-random numbers) was
|
||||||
introduced with Postfix version 2.5.
|
introduced with Postfix version 2.5.
|
||||||
@ -247,9 +249,11 @@ The following is an extensive list of names and values.
|
|||||||
| |at compile time: |
|
| |at compile time: |
|
||||||
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|
||||||
|| |Do not build with Berkeley DB support. By |
|
|| |Do not build with Berkeley DB support. By |
|
||||||
||-DNO_DB |default, Berkeley DB support is compiled in on|
|
|| |default, Berkeley DB support is compiled in on|
|
||||||
|| |platforms that are known to support this |
|
||-DNO_DB |platforms that are known to support this |
|
||||||
|| |feature. |
|
|| |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. |
|
|| |Do not build with Solaris /dev/poll support. |
|
||||||
||-DNO_DEVPOLL |By default, /dev/poll support is compiled in |
|
||-DNO_DEVPOLL |By default, /dev/poll support is compiled in |
|
||||||
|
@ -4,14 +4,6 @@ PPoossttffiixx OOppeennLLDDAAPP LLMMDDBB HHoowwttoo
|
|||||||
|
|
||||||
IInnttrroodduuccttiioonn
|
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 uses databases of various kinds to store and look up information.
|
||||||
Postfix databases are specified as "type:name". OpenLDAP LMDB implements the
|
Postfix databases are specified as "type:name". OpenLDAP LMDB implements the
|
||||||
Postfix database type "lmdb". The name of a Postfix OpenLDAP LMDB database is
|
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
|
BBuuiillddiinngg PPoossttffiixx wwiitthh OOppeennLLDDAAPP LLMMDDBB ssuuppppoorrtt
|
||||||
|
|
||||||
Postfix normally does not enable OpenLDAP LMDB support. To build Postfix after
|
Postfix normally does not enable OpenLDAP LMDB support. To build Postfix with
|
||||||
you installed OpenLDAP LMDB from source code, use something like:
|
OpenLDAP LMDB support, use something like:
|
||||||
|
|
||||||
% make makefiles CCARGS="-DHAS_LMDB -I/usr/local/include" \
|
% make makefiles CCARGS="-DHAS_LMDB -I/usr/local/include" \
|
||||||
AUXLIBS="-L/usr/local/lib -llmdb"
|
AUXLIBS="-L/usr/local/lib -llmdb"
|
||||||
% make
|
% make
|
||||||
|
|
||||||
Solaris needs this:
|
Solaris may need this:
|
||||||
|
|
||||||
% make makefiles CCARGS="-DHAS_LMDB -I/usr/local/include" \
|
% make makefiles CCARGS="-DHAS_LMDB -I/usr/local/include" \
|
||||||
AUXLIBS="-R/usr/local/lib -L/usr/local/lib -llmdb"
|
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
|
CCoonnffiigguurree LLMMDDBB sseettttiinnggss
|
||||||
|
|
||||||
Postfix provides a configuration parameter that controls how large an OpenLDAP
|
Postfix provides one configuration parameter that controls OpenLDAP LMDB
|
||||||
LMDB database may grow.
|
database behavior.
|
||||||
|
|
||||||
* lmdb_map_size (default: 16 MBytes per table). This setting controls how
|
* lmdb_map_size (default: 16777216). This setting specifies the initial
|
||||||
large any OpenLDAP LMDB database may grow. It must be set large enough to
|
OpenLDAP LMDB database size limit in bytes. Each time a database becomes
|
||||||
accommodate the largest table that Postfix will use.
|
full, its size limit is doubled.
|
||||||
|
|
||||||
MMiissssiinngg pptthhrreeaadd lliibbrraarryy ttrroouubbllee
|
MMiissssiinngg pptthhrreeaadd lliibbrraarryy ttrroouubbllee
|
||||||
|
|
||||||
@ -72,81 +64,17 @@ information is available at http://highlandsun.com/hyc/mdb/.
|
|||||||
UUnneexxppeecctteedd ffaaiilluurree mmooddeess ooff PPoossttffiixx LLMMDDBB ddaattaabbaasseess..
|
UUnneexxppeecctteedd ffaaiilluurree mmooddeess ooff PPoossttffiixx LLMMDDBB ddaattaabbaasseess..
|
||||||
|
|
||||||
As documented below, conversion to LMDB introduces a number of failure modes
|
As documented below, conversion to LMDB introduces a number of failure modes
|
||||||
that don't exist with other Postfix databases.
|
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
|
||||||
UUnneexxppeecctteedd ppoossttmmaapp((11))//ppoossttaalliiaass((11)) ""ddaattaabbaassee ffuullll"" eerrrroorrss..
|
of LMDB 0.9.8.
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
NNoonn--oobbvviioouuss rreeccoovveerryy wwiitthh ppoossttmmaapp((11))//ppoossttaalliiaass((11))//ttllssmmggrr((88)) ffrroomm aa ccoorrrruupptteedd
|
NNoonn--oobbvviioouuss rreeccoovveerryy wwiitthh ppoossttmmaapp((11))//ppoossttaalliiaass((11))//ttllssmmggrr((88)) ffrroomm aa ccoorrrruupptteedd
|
||||||
ddaattaabbaassee..
|
ddaattaabbaassee..
|
||||||
|
|
||||||
Problem:
|
Problem:
|
||||||
You cannot rebuild a corrupted LMDB database simply by running postmap(1)
|
You cannot rebuild a corrupted LMDB database simply by re-running postmap
|
||||||
or postalias(1), or by waiting until the tlsmgr(8) daemon restarts
|
(1) or postalias(1), or by waiting until the tlsmgr(8) daemon restarts.
|
||||||
automatically. This problem does not exist with other Postfix databases.
|
This problem does not exist with other Postfix databases.
|
||||||
|
|
||||||
Background:
|
Background:
|
||||||
The Postfix LMDB database client does not truncate the database file.
|
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.
|
Postfix does not process mail until someone fixes the problem.
|
||||||
|
|
||||||
Recovery:
|
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
|
postmap(1) or postalias(1) command, or wait until the tlsmgr(8) daemon
|
||||||
restarts automatically.
|
restarts.
|
||||||
|
|
||||||
Prevention:
|
Prevention:
|
||||||
Arrange your file systems such that they never run out of free space.
|
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
|
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.
|
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
|
reject_unauthenticated_sender_login_mismatch for additional control over the
|
||||||
SASL login name and the envelope sender.
|
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:
|
Topics covered in this document:
|
||||||
|
|
||||||
* Symptoms of Postfix SMTP server overload
|
* Symptoms of Postfix SMTP server overload
|
||||||
|
* Automatic stress-adaptive behavior
|
||||||
* Service more SMTP clients at the same time
|
* Service more SMTP clients at the same time
|
||||||
* Spend less time per SMTP client
|
* Spend less time per SMTP client
|
||||||
* Disconnect suspicious SMTP clients
|
* Disconnect suspicious SMTP clients
|
||||||
* Temporary measures for older Postfix releases
|
* Temporary measures for older Postfix releases
|
||||||
* Automatic stress-adaptive behavior
|
|
||||||
* Detecting support for stress-adaptive behavior
|
* Detecting support for stress-adaptive behavior
|
||||||
* Forcing stress-adaptive behavior on or off
|
* Forcing stress-adaptive behavior on or off
|
||||||
* Other measures to off-load zombies
|
* 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
|
Oct 3 20:39:27 spike postfix/master[28905]: warning: to avoid this
|
||||||
condition, increase the process count in master.cf or reduce the
|
condition, increase the process count in master.cf or reduce the
|
||||||
service time per client
|
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
|
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
|
server overload is not necessarily lost. It should still arrive once the
|
||||||
situation returns to normal, as long as the overload condition is temporary.
|
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
|
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
|
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
|
more SMTP clients simultaneously. For this you need to increase the number of
|
||||||
Postfix SMTP server processes. This will improve the responsiveness for remote
|
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
|
only temporarily. The next section of this document introduces a way to
|
||||||
automate this process.
|
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
|
DDeetteeccttiinngg ssuuppppoorrtt ffoorr ssttrreessss--aaddaappttiivvee bbeehhaavviioorr
|
||||||
|
|
||||||
To find out if your Postfix installation supports stress-adaptive behavior, use
|
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
|
If you upgrade from Postfix 2.9 or earlier, read RELEASE_NOTES-2.10
|
||||||
before proceeding.
|
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
|
Major changes with snapshot 20130602
|
||||||
====================================
|
====================================
|
||||||
|
|
||||||
|
@ -16,6 +16,8 @@ Wish list:
|
|||||||
Begin code revision, after DANE support stabilizes. This
|
Begin code revision, after DANE support stabilizes. This
|
||||||
should be one pass that changes only names and no code.
|
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
|
All source code must specify its original author and
|
||||||
license statement. Some code modules specify Lutz Jaenicke
|
license statement. Some code modules specify Lutz Jaenicke
|
||||||
as the original author and fall under his liberal license.
|
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>
|
<tr> <td>DEF_CONFIG_DIR</td> <td><a href="postconf.5.html#config_directory">config_directory</a></td>
|
||||||
<td>/etc/postfix</td> </tr>
|
<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>
|
<tr> <td>DEF_DAEMON_DIR</td> <td><a href="postconf.5.html#daemon_directory">daemon_directory</a></td>
|
||||||
<td>/usr/libexec/postfix</td> </tr>
|
<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
|
<tr> <td> </td> <td> -DNO_DB </td> <td> Do not build with Berkeley
|
||||||
DB support. By default, Berkeley DB support is compiled in on
|
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
|
<tr> <td> </td> <td> -DNO_DEVPOLL </td> <td> Do not build with
|
||||||
Solaris <tt>/dev/poll</tt> support. By default, <tt>/dev/poll</tt>
|
Solaris <tt>/dev/poll</tt> support. By default, <tt>/dev/poll</tt>
|
||||||
|
@ -19,14 +19,6 @@
|
|||||||
|
|
||||||
<h2>Introduction</h2>
|
<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
|
<p> Postfix uses databases of various kinds to store and look up
|
||||||
information. Postfix databases are specified as "type:name".
|
information. Postfix databases are specified as "type:name".
|
||||||
OpenLDAP LMDB implements the Postfix database type "lmdb".
|
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>
|
<h2><a name="with_lmdb">Building Postfix with OpenLDAP LMDB support</a></h2>
|
||||||
|
|
||||||
<p> Postfix normally does not enable OpenLDAP LMDB support.
|
<p> Postfix normally does not enable OpenLDAP LMDB support. To
|
||||||
To build Postfix after you installed OpenLDAP LMDB from
|
build Postfix with OpenLDAP LMDB support, use something like: </p>
|
||||||
source code, use something like: </p>
|
|
||||||
|
|
||||||
<blockquote>
|
<blockquote>
|
||||||
<pre>
|
<pre>
|
||||||
@ -64,7 +55,7 @@ source code, use something like: </p>
|
|||||||
</pre>
|
</pre>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
|
|
||||||
<p> Solaris needs this: </p>
|
<p> Solaris may need this: </p>
|
||||||
|
|
||||||
<blockquote>
|
<blockquote>
|
||||||
<pre>
|
<pre>
|
||||||
@ -78,15 +69,14 @@ source code, use something like: </p>
|
|||||||
|
|
||||||
<h2><a name="configure">Configure LMDB settings</a></h2>
|
<h2><a name="configure">Configure LMDB settings</a></h2>
|
||||||
|
|
||||||
<p> Postfix provides a configuration parameter that controls how
|
<p> Postfix provides one configuration parameter that controls
|
||||||
large an OpenLDAP LMDB database may grow. </p>
|
OpenLDAP LMDB database behavior. </p>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
|
|
||||||
<li> <p> <a href="postconf.5.html#lmdb_map_size">lmdb_map_size</a> (default: 16 MBytes per table). This setting
|
<li> <p> <a href="postconf.5.html#lmdb_map_size">lmdb_map_size</a> (default: 16777216). This setting specifies
|
||||||
controls how large any OpenLDAP LMDB database may grow. It must be
|
the initial OpenLDAP LMDB database size limit in bytes. Each time
|
||||||
set large enough to accommodate the largest table that Postfix will
|
a database becomes full, its size limit is doubled. </p>
|
||||||
use. </p>
|
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
@ -119,15 +109,17 @@ More information is available at
|
|||||||
databases. </a> </h2>
|
databases. </a> </h2>
|
||||||
|
|
||||||
<p> As documented below, conversion to LMDB introduces a number of
|
<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"
|
<p> <strong>Unexpected <a href="postmap.1.html">postmap(1)</a>/<a href="postalias.1.html">postalias(1)</a> "database full"
|
||||||
errors. </strong></p>
|
errors. </strong></p>
|
||||||
|
|
||||||
<dl>
|
<dl>
|
||||||
|
|
||||||
<!--
|
|
||||||
|
|
||||||
<dt> Problem: </dt> <dd> <p> The "postmap <a href="LMDB_README.html">lmdb</a>:filename" command
|
<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
|
fails with an MDB_TXN_FULL error. This problem does not exist with
|
||||||
other Postfix databases. </p> </dd>
|
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
|
with the database size, and so that all "out of storage" errors are
|
||||||
resolved by increasing the database size. </p> </dd>
|
resolved by increasing the database size. </p> </dd>
|
||||||
|
|
||||||
-->
|
|
||||||
|
|
||||||
<dt> Problem: </dt> <dd> <p> The "postmap <a href="LMDB_README.html">lmdb</a>:filename" command
|
<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
|
fails with an MDB_MAP_FULL error. This problem does not exist with
|
||||||
other Postfix databases. </p> </dd>
|
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
|
MDB_MAP_FULL error, it expands the database file size to the current
|
||||||
LMDB map size limit before terminating. </p>
|
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
|
<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,
|
||||||
larger than <a href="postconf.5.html#lmdb_map_size">lmdb_map_size</a>/3, it logs a warning and uses a larger
|
it discovers that the LMDB file is larger than <a href="postconf.5.html#lmdb_map_size">lmdb_map_size</a>/3,
|
||||||
size limit instead: </p>
|
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 ≥
|
<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>
|
(lmdb map size limit 16777216)/3<br> warning: <i>filename</i>.<a href="LMDB_README.html">lmdb</a>:
|
||||||
</p>
|
using map size limit 45072384</tt> </p>
|
||||||
|
|
||||||
<p> The two steps above can be used to automate recovery and avoid
|
<p> By repeating the two steps above you can automate recovery and
|
||||||
the need for human intervention. Just repeat "postmap <a href="LMDB_README.html">lmdb</a>:filename"
|
avoid the need for human intervention. Just repeat "postmap
|
||||||
(up to some limit). After each failure it will use a 3x larger
|
<a href="LMDB_README.html">lmdb</a>:filename" (up to some limit). After each failure it will use
|
||||||
size limit, and eventually the "database full" error will disappear.
|
a 3x larger size limit, and eventually the "database full" error
|
||||||
</p>
|
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
|
<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>
|
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
|
||||||
</dd> </dl>
|
size. </p> </dd> </dl>
|
||||||
|
|
||||||
<p> <strong>Unexpected Postfix daemon "database full" errors.
|
<p> <strong>Unexpected Postfix daemon "database full" errors.
|
||||||
</strong></p>
|
</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>
|
larger size limit instead: </p>
|
||||||
|
|
||||||
<p> <tt> warning: <i>filename</i>.<a href="LMDB_README.html">lmdb</a>: file size 15024128 ≥
|
<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>
|
(lmdb map size limit 16777216)/3 <br>warning: <i>filename</i>.<a href="LMDB_README.html">lmdb</a>:
|
||||||
</p>
|
using map size limit 45072384</tt> </p>
|
||||||
|
|
||||||
<p> This can be used to automate recovery and avoid the need for
|
<p> This can be used to automate recovery and avoid the need for
|
||||||
human intervention. Each time the daemon runs into a "database full"
|
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>
|
sure that <a href="postconf.5.html#lmdb_map_size">lmdb_map_size</a> > 3x the largest LMDB file size. </p>
|
||||||
</dd> </dl>
|
</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>
|
<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>
|
from a corrupted database. </strong></p>
|
||||||
|
|
||||||
<dl>
|
<dl>
|
||||||
|
|
||||||
<dt> Problem: </dt> <dd> <p> You cannot rebuild a corrupted LMDB
|
<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
|
database simply by re-running <a href="postmap.1.html">postmap(1)</a> or <a href="postalias.1.html">postalias(1)</a>, or by
|
||||||
until the <a href="tlsmgr.8.html">tlsmgr(8)</a> daemon restarts automatically. This problem
|
waiting until the <a href="tlsmgr.8.html">tlsmgr(8)</a> daemon restarts. This problem
|
||||||
does not exist with other Postfix databases. </p> </dd>
|
does not exist with other Postfix databases. </p> </dd>
|
||||||
|
|
||||||
<dt> Background: </dt> <dd> <p> The Postfix LMDB database client
|
<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
|
<dt> Impact: </dt> <dd> <p> Postfix does not process mail until
|
||||||
someone fixes the problem. </p> </dd>
|
someone fixes the problem. </p> </dd>
|
||||||
|
|
||||||
<dt> Recovery: </dt> <dd> <p> First delete the ".lmdb" file by hand,
|
<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,
|
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>
|
or wait until the <a href="tlsmgr.8.html">tlsmgr(8)</a> daemon restarts. </p>
|
||||||
</dd>
|
</dd>
|
||||||
|
|
||||||
<dt> Prevention: </dt> <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
|
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>
|
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
|
<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>
|
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="#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="#concurrency"> Service more SMTP clients at the same time </a>
|
||||||
|
|
||||||
<li><a href="#time"> Spend less time per SMTP client </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="#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="#feature"> Detecting support for stress-adaptive behavior </a>
|
||||||
|
|
||||||
<li><a href="#forcing"> Forcing stress-adaptive behavior on or off </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
|
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
|
condition, increase the process count in <a href="master.5.html">master.cf</a> or reduce the
|
||||||
service time per client
|
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>
|
</pre>
|
||||||
|
|
||||||
</ul>
|
</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
|
still arrive once the situation returns to normal, as long as the
|
||||||
overload condition is temporary. </p>
|
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>
|
<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
|
<p> One measure to avoid the "all server processes busy" condition
|
||||||
is to service more SMTP clients simultaneously. For this you need
|
is to service more SMTP clients simultaneously. For this you need
|
||||||
to increase the number of Postfix SMTP server processes. This will
|
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
|
as these measures are used only temporarily. The next section of
|
||||||
this document introduces a way to automate this process. </p>
|
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>
|
<h2><a name="feature"> Detecting support for stress-adaptive behavior </a></h2>
|
||||||
|
|
||||||
<p> To find out if your Postfix installation supports stress-adaptive
|
<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
|
ldap_table - Postfix LDAP client configuration
|
||||||
|
|
||||||
<b>SYNOPSIS</b>
|
<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>
|
<b>DESCRIPTION</b>
|
||||||
The Postfix mail system uses optional tables for address
|
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-
|
the <b>demand</b> value of <b>TLS_REQCERT</b> in LDAP client con-
|
||||||
figuration files.
|
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
|
equivalents here. They are not available with
|
||||||
OpenLDAP 2.0, and in any case have questionable
|
OpenLDAP 2.0, and in any case have questionable
|
||||||
security properties. Either you want TLS verified
|
security properties. Either you want TLS verified
|
||||||
|
@ -3782,11 +3782,11 @@ this length; upon delivery, long lines are reconstructed. </p>
|
|||||||
</DD>
|
</DD>
|
||||||
|
|
||||||
<DT><b><a name="lmdb_map_size">lmdb_map_size</a>
|
<DT><b><a name="lmdb_map_size">lmdb_map_size</a>
|
||||||
(default: 10485760)</b></DT><DD>
|
(default: 16777216)</b></DT><DD>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
The per-table size limit for programs that create OpenLDAP LMDB
|
The initial OpenLDAP LMDB database size limit in bytes. Each time
|
||||||
tables. Specify a byte count.
|
a database becomes full, its size limit is doubled.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<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>
|
<DT><b><a name="smtpd_per_record_deadline">smtpd_per_record_deadline</a>
|
||||||
(default: normal: no, overload: yes)</b></DT><DD>
|
(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
|
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
|
or receive a complete record (an SMTP command line, SMTP response
|
||||||
line, SMTP message content line, or TLS protocol message). This
|
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
|
authenticated clients only. This feature is available in
|
||||||
Postfix version 2.1 and later. </dd>
|
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>
|
<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
|
<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
|
where a non-whitelisted remote SMTP client can
|
||||||
obtain <a href="postscreen.8.html"><b>postscreen</b>(8)</a>'s temporary whitelist status.
|
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
|
These tests are executed before the remote SMTP client
|
||||||
receives the "220 servername" greeting. If no tests remain
|
receives the "220 servername" greeting. If no tests remain
|
||||||
after the successful completion of this phase, the client
|
after the successful completion of this phase, the client
|
||||||
@ -253,13 +253,13 @@ POSTSCREEN(8) POSTSCREEN(8)
|
|||||||
combined DNSBL score as defined with the
|
combined DNSBL score as defined with the
|
||||||
<a href="postconf.5.html#postscreen_dnsbl_sites">postscreen_dnsbl_sites</a> parameter.
|
<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
|
These tests are executed after the remote SMTP client
|
||||||
receives the "220 servername" greeting. If a client passes
|
receives the "220 servername" greeting. If a client passes
|
||||||
all tests during this phase, it will receive a 4XX
|
all tests during this phase, it will receive a 4XX
|
||||||
response to RCPT TO commands until the client hangs up.
|
response to all RCPT TO commands. After the client recon-
|
||||||
After this, the client will be allowed to talk directly to
|
nects, it will be allowed to talk directly to a Postfix
|
||||||
a Postfix SMTP server process.
|
SMTP server process.
|
||||||
|
|
||||||
<b><a href="postconf.5.html#postscreen_bare_newline_action">postscreen_bare_newline_action</a> (ignore)</b>
|
<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
|
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
|
\fBdemand\fR value of \fBTLS_REQCERT\fR in LDAP client configuration
|
||||||
files.
|
files.
|
||||||
.sp
|
.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
|
here. They are not available with OpenLDAP 2.0, and in any case have
|
||||||
questionable security properties. Either you want TLS verified LDAP
|
questionable security properties. Either you want TLS verified LDAP
|
||||||
connections, or you don't.
|
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)
|
.SH line_length_limit (default: 2048)
|
||||||
Upon input, long lines are chopped up into pieces of at most
|
Upon input, long lines are chopped up into pieces of at most
|
||||||
this length; upon delivery, long lines are reconstructed.
|
this length; upon delivery, long lines are reconstructed.
|
||||||
.SH lmdb_map_size (default: 10485760)
|
.SH lmdb_map_size (default: 16777216)
|
||||||
The per-table size limit for programs that create OpenLDAP LMDB
|
The initial OpenLDAP LMDB database size limit in bytes. Each time
|
||||||
tables. Specify a byte count.
|
a database becomes full, its size limit is doubled.
|
||||||
.PP
|
.PP
|
||||||
This feature is available in Postfix 2.11 and later.
|
This feature is available in Postfix 2.11 and later.
|
||||||
.SH lmtp_address_preference (default: ipv6)
|
.SH lmtp_address_preference (default: ipv6)
|
||||||
@ -9034,7 +9034,8 @@ DNS lookup and increases the maximal inbound delivery rate.
|
|||||||
.PP
|
.PP
|
||||||
This feature is available in Postfix 2.3 and later.
|
This feature is available in Postfix 2.3 and later.
|
||||||
.SH smtpd_per_record_deadline (default: normal: no, overload: yes)
|
.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
|
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
|
or receive a complete record (an SMTP command line, SMTP response
|
||||||
line, SMTP message content line, or TLS protocol message). This
|
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
|
authenticated clients only. This feature is available in
|
||||||
Postfix version 2.1 and later.
|
Postfix version 2.1 and later.
|
||||||
.br
|
.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"
|
.IP "\fBreject_non_fqdn_sender\fR"
|
||||||
Reject the request when the MAIL FROM address is not in
|
Reject the request when the MAIL FROM address is not in
|
||||||
fully-qualified domain form, as required by the RFC.
|
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
|
A list of local \fBpostscreen\fR(8) server IP addresses where a
|
||||||
non-whitelisted remote SMTP client can obtain \fBpostscreen\fR(8)'s temporary
|
non-whitelisted remote SMTP client can obtain \fBpostscreen\fR(8)'s temporary
|
||||||
whitelist status.
|
whitelist status.
|
||||||
.SH "BEFORE-GREETING TESTS"
|
.SH "BEFORE 220 GREETING TESTS"
|
||||||
.na
|
.na
|
||||||
.nf
|
.nf
|
||||||
.ad
|
.ad
|
||||||
@ -248,7 +248,7 @@ Available in Postfix version 2.11 and later:
|
|||||||
Allow a remote SMTP client to skip "before" and "after 220
|
Allow a remote SMTP client to skip "before" and "after 220
|
||||||
greeting" protocol tests, based on its combined DNSBL score as
|
greeting" protocol tests, based on its combined DNSBL score as
|
||||||
defined with the postscreen_dnsbl_sites parameter.
|
defined with the postscreen_dnsbl_sites parameter.
|
||||||
.SH "AFTER-GREETING TESTS"
|
.SH "AFTER 220 GREETING TESTS"
|
||||||
.na
|
.na
|
||||||
.nf
|
.nf
|
||||||
.ad
|
.ad
|
||||||
@ -256,9 +256,9 @@ defined with the postscreen_dnsbl_sites parameter.
|
|||||||
These tests are executed after the remote SMTP client
|
These tests are executed after the remote SMTP client
|
||||||
receives the "220 servername" greeting. If a client passes
|
receives the "220 servername" greeting. If a client passes
|
||||||
all tests during this phase, it will receive a 4XX response
|
all tests during this phase, it will receive a 4XX response
|
||||||
to RCPT TO commands until the client hangs up. After this,
|
to all RCPT TO commands. After the client reconnects, it
|
||||||
the client will be allowed to talk directly to a Postfix
|
will be allowed to talk directly to a Postfix SMTP server
|
||||||
SMTP server process.
|
process.
|
||||||
.IP "\fBpostscreen_bare_newline_action (ignore)\fR"
|
.IP "\fBpostscreen_bare_newline_action (ignore)\fR"
|
||||||
The action that \fBpostscreen\fR(8) takes when a remote SMTP client sends
|
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
|
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_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;\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;\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_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_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;
|
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>
|
<tr> <td>DEF_CONFIG_DIR</td> <td>config_directory</td>
|
||||||
<td>/etc/postfix</td> </tr>
|
<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>
|
<tr> <td>DEF_DAEMON_DIR</td> <td>daemon_directory</td>
|
||||||
<td>/usr/libexec/postfix</td> </tr>
|
<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
|
<tr> <td> </td> <td> -DNO_DB </td> <td> Do not build with Berkeley
|
||||||
DB support. By default, Berkeley DB support is compiled in on
|
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
|
<tr> <td> </td> <td> -DNO_DEVPOLL </td> <td> Do not build with
|
||||||
Solaris <tt>/dev/poll</tt> support. By default, <tt>/dev/poll</tt>
|
Solaris <tt>/dev/poll</tt> support. By default, <tt>/dev/poll</tt>
|
||||||
|
@ -19,14 +19,6 @@
|
|||||||
|
|
||||||
<h2>Introduction</h2>
|
<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
|
<p> Postfix uses databases of various kinds to store and look up
|
||||||
information. Postfix databases are specified as "type:name".
|
information. Postfix databases are specified as "type:name".
|
||||||
OpenLDAP LMDB implements the Postfix database type "lmdb".
|
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>
|
<h2><a name="with_lmdb">Building Postfix with OpenLDAP LMDB support</a></h2>
|
||||||
|
|
||||||
<p> Postfix normally does not enable OpenLDAP LMDB support.
|
<p> Postfix normally does not enable OpenLDAP LMDB support. To
|
||||||
To build Postfix after you installed OpenLDAP LMDB from
|
build Postfix with OpenLDAP LMDB support, use something like: </p>
|
||||||
source code, use something like: </p>
|
|
||||||
|
|
||||||
<blockquote>
|
<blockquote>
|
||||||
<pre>
|
<pre>
|
||||||
@ -64,7 +55,7 @@ source code, use something like: </p>
|
|||||||
</pre>
|
</pre>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
|
|
||||||
<p> Solaris needs this: </p>
|
<p> Solaris may need this: </p>
|
||||||
|
|
||||||
<blockquote>
|
<blockquote>
|
||||||
<pre>
|
<pre>
|
||||||
@ -78,15 +69,14 @@ source code, use something like: </p>
|
|||||||
|
|
||||||
<h2><a name="configure">Configure LMDB settings</a></h2>
|
<h2><a name="configure">Configure LMDB settings</a></h2>
|
||||||
|
|
||||||
<p> Postfix provides a configuration parameter that controls how
|
<p> Postfix provides one configuration parameter that controls
|
||||||
large an OpenLDAP LMDB database may grow. </p>
|
OpenLDAP LMDB database behavior. </p>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
|
|
||||||
<li> <p> lmdb_map_size (default: 16 MBytes per table). This setting
|
<li> <p> lmdb_map_size (default: 16777216). This setting specifies
|
||||||
controls how large any OpenLDAP LMDB database may grow. It must be
|
the initial OpenLDAP LMDB database size limit in bytes. Each time
|
||||||
set large enough to accommodate the largest table that Postfix will
|
a database becomes full, its size limit is doubled. </p>
|
||||||
use. </p>
|
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
@ -119,15 +109,17 @@ http://highlandsun.com/hyc/mdb/. </p>
|
|||||||
databases. </a> </h2>
|
databases. </a> </h2>
|
||||||
|
|
||||||
<p> As documented below, conversion to LMDB introduces a number of
|
<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"
|
<p> <strong>Unexpected postmap(1)/postalias(1) "database full"
|
||||||
errors. </strong></p>
|
errors. </strong></p>
|
||||||
|
|
||||||
<dl>
|
<dl>
|
||||||
|
|
||||||
<!--
|
|
||||||
|
|
||||||
<dt> Problem: </dt> <dd> <p> The "postmap lmdb:filename" command
|
<dt> Problem: </dt> <dd> <p> The "postmap lmdb:filename" command
|
||||||
fails with an MDB_TXN_FULL error. This problem does not exist with
|
fails with an MDB_TXN_FULL error. This problem does not exist with
|
||||||
other Postfix databases. </p> </dd>
|
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
|
with the database size, and so that all "out of storage" errors are
|
||||||
resolved by increasing the database size. </p> </dd>
|
resolved by increasing the database size. </p> </dd>
|
||||||
|
|
||||||
-->
|
|
||||||
|
|
||||||
<dt> Problem: </dt> <dd> <p> The "postmap lmdb:filename" command
|
<dt> Problem: </dt> <dd> <p> The "postmap lmdb:filename" command
|
||||||
fails with an MDB_MAP_FULL error. This problem does not exist with
|
fails with an MDB_MAP_FULL error. This problem does not exist with
|
||||||
other Postfix databases. </p> </dd>
|
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
|
MDB_MAP_FULL error, it expands the database file size to the current
|
||||||
LMDB map size limit before terminating. </p>
|
LMDB map size limit before terminating. </p>
|
||||||
|
|
||||||
<p> When the postmap(1) or postalias(1) command opens an LMDB file
|
<p> Next, when you re-run the postmap(1) or postalias(1) command,
|
||||||
larger than lmdb_map_size/3, it logs a warning and uses a larger
|
it discovers that the LMDB file is larger than lmdb_map_size/3,
|
||||||
size limit instead: </p>
|
logs a warning, and uses a larger LMDB map size limit instead: </p>
|
||||||
|
|
||||||
<p> <tt> warning: <i>filename</i>.lmdb: file size 15024128 ≥
|
<p> <tt> warning: <i>filename</i>.lmdb: file size 15024128 ≥
|
||||||
(lmdb map size limit 16777216)/3 -- using a larger map size limit</tt>
|
(lmdb map size limit 16777216)/3<br> warning: <i>filename</i>.lmdb:
|
||||||
</p>
|
using map size limit 45072384</tt> </p>
|
||||||
|
|
||||||
<p> The two steps above can be used to automate recovery and avoid
|
<p> By repeating the two steps above you can automate recovery and
|
||||||
the need for human intervention. Just repeat "postmap lmdb:filename"
|
avoid the need for human intervention. Just repeat "postmap
|
||||||
(up to some limit). After each failure it will use a 3x larger
|
lmdb:filename" (up to some limit). After each failure it will use
|
||||||
size limit, and eventually the "database full" error will disappear.
|
a 3x larger size limit, and eventually the "database full" error
|
||||||
</p>
|
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
|
<dt> Prevention: </dt> <dd> <p> Monitor your LMDB files and make
|
||||||
sure that lmdb_map_size > 3x the largest LMDB file size. </p>
|
sure that in main.cf, lmdb_map_size > 3x the largest LMDB file
|
||||||
</dd> </dl>
|
size. </p> </dd> </dl>
|
||||||
|
|
||||||
<p> <strong>Unexpected Postfix daemon "database full" errors.
|
<p> <strong>Unexpected Postfix daemon "database full" errors.
|
||||||
</strong></p>
|
</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>
|
larger size limit instead: </p>
|
||||||
|
|
||||||
<p> <tt> warning: <i>filename</i>.lmdb: file size 15024128 ≥
|
<p> <tt> warning: <i>filename</i>.lmdb: file size 15024128 ≥
|
||||||
(lmdb map size limit 16777216)/3 -- using a larger map size limit</tt>
|
(lmdb map size limit 16777216)/3 <br>warning: <i>filename</i>.lmdb:
|
||||||
</p>
|
using map size limit 45072384</tt> </p>
|
||||||
|
|
||||||
<p> This can be used to automate recovery and avoid the need for
|
<p> This can be used to automate recovery and avoid the need for
|
||||||
human intervention. Each time the daemon runs into a "database full"
|
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>
|
sure that lmdb_map_size > 3x the largest LMDB file size. </p>
|
||||||
</dd> </dl>
|
</dd> </dl>
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
<p> <strong>Non-obvious recovery with postmap(1)/postalias(1)/tlsmgr(8)
|
<p> <strong>Non-obvious recovery with postmap(1)/postalias(1)/tlsmgr(8)
|
||||||
from a corrupted database. </strong></p>
|
from a corrupted database. </strong></p>
|
||||||
|
|
||||||
<dl>
|
<dl>
|
||||||
|
|
||||||
<dt> Problem: </dt> <dd> <p> You cannot rebuild a corrupted LMDB
|
<dt> Problem: </dt> <dd> <p> You cannot rebuild a corrupted LMDB
|
||||||
database simply by running postmap(1) or postalias(1), or by waiting
|
database simply by re-running postmap(1) or postalias(1), or by
|
||||||
until the tlsmgr(8) daemon restarts automatically. This problem
|
waiting until the tlsmgr(8) daemon restarts. This problem
|
||||||
does not exist with other Postfix databases. </p> </dd>
|
does not exist with other Postfix databases. </p> </dd>
|
||||||
|
|
||||||
<dt> Background: </dt> <dd> <p> The Postfix LMDB database client
|
<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
|
<dt> Impact: </dt> <dd> <p> Postfix does not process mail until
|
||||||
someone fixes the problem. </p> </dd>
|
someone fixes the problem. </p> </dd>
|
||||||
|
|
||||||
<dt> Recovery: </dt> <dd> <p> First delete the ".lmdb" file by hand,
|
<dt> Recovery: </dt> <dd> <p> First delete the ".lmdb" file by hand.
|
||||||
then rebuild the file with the postmap(1) or postalias(1) command,
|
Then, rebuild the file with the postmap(1) or postalias(1) command,
|
||||||
or wait until the tlsmgr(8) daemon restarts automatically. </p>
|
or wait until the tlsmgr(8) daemon restarts. </p>
|
||||||
</dd>
|
</dd>
|
||||||
|
|
||||||
<dt> Prevention: </dt> <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
|
command if <code>smtpd_sender_login_maps</code> does not specify
|
||||||
the SMTP client's login name as an owner of that address. </p>
|
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
|
<code>reject_unauthenticated_sender_login_mismatch</code> for additional
|
||||||
control over the SASL login name and the envelope sender. </p>
|
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="#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="#concurrency"> Service more SMTP clients at the same time </a>
|
||||||
|
|
||||||
<li><a href="#time"> Spend less time per SMTP client </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="#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="#feature"> Detecting support for stress-adaptive behavior </a>
|
||||||
|
|
||||||
<li><a href="#forcing"> Forcing stress-adaptive behavior on or off </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
|
Oct 3 20:39:27 spike postfix/master[28905]: warning: to avoid this
|
||||||
condition, increase the process count in master.cf or reduce the
|
condition, increase the process count in master.cf or reduce the
|
||||||
service time per client
|
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>
|
</pre>
|
||||||
|
|
||||||
</ul>
|
</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
|
still arrive once the situation returns to normal, as long as the
|
||||||
overload condition is temporary. </p>
|
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>
|
<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
|
<p> One measure to avoid the "all server processes busy" condition
|
||||||
is to service more SMTP clients simultaneously. For this you need
|
is to service more SMTP clients simultaneously. For this you need
|
||||||
to increase the number of Postfix SMTP server processes. This will
|
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
|
as these measures are used only temporarily. The next section of
|
||||||
this document introduces a way to automate this process. </p>
|
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>
|
<h2><a name="feature"> Detecting support for stress-adaptive behavior </a></h2>
|
||||||
|
|
||||||
<p> To find out if your Postfix installation supports stress-adaptive
|
<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
|
# \fBdemand\fR value of \fBTLS_REQCERT\fR in LDAP client configuration
|
||||||
# files.
|
# files.
|
||||||
# .sp
|
# .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
|
# here. They are not available with OpenLDAP 2.0, and in any case have
|
||||||
# questionable security properties. Either you want TLS verified LDAP
|
# questionable security properties. Either you want TLS verified LDAP
|
||||||
# connections, or you don't.
|
# 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.
|
Specify 0 when mail delivery should be tried only once.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
%PARAM lmdb_map_size 10485760
|
%PARAM lmdb_map_size 16777216
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
The per-table size limit for programs that create OpenLDAP LMDB
|
The initial OpenLDAP LMDB database size limit in bytes. Each time
|
||||||
tables. Specify a byte count.
|
a database becomes full, its size limit is doubled.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
@ -6292,6 +6292,12 @@ feature is available in Postfix 2.1 and later. </dd>
|
|||||||
authenticated clients only. This feature is available in
|
authenticated clients only. This feature is available in
|
||||||
Postfix version 2.1 and later. </dd>
|
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>
|
<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
|
<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
|
%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
|
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
|
or receive a complete record (an SMTP command line, SMTP response
|
||||||
line, SMTP message content line, or TLS protocol message). This
|
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_SENDER_LOGIN_MISMATCH "reject_sender_login_mismatch"
|
||||||
#define REJECT_AUTH_SENDER_LOGIN_MISMATCH \
|
#define REJECT_AUTH_SENDER_LOGIN_MISMATCH \
|
||||||
"reject_authenticated_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 \
|
#define REJECT_UNAUTH_SENDER_LOGIN_MISMATCH \
|
||||||
"reject_unauthenticated_sender_login_mismatch"
|
"reject_unauthenticated_sender_login_mismatch"
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
* Patches change both the patchlevel and the release date. Snapshots have no
|
* Patches change both the patchlevel and the release date. Snapshots have no
|
||||||
* patchlevel; they change the release date only.
|
* patchlevel; they change the release date only.
|
||||||
*/
|
*/
|
||||||
#define MAIL_RELEASE_DATE "20130825"
|
#define MAIL_RELEASE_DATE "20130927"
|
||||||
#define MAIL_VERSION_NUMBER "2.11"
|
#define MAIL_VERSION_NUMBER "2.11"
|
||||||
|
|
||||||
#ifdef SNAPSHOT
|
#ifdef SNAPSHOT
|
||||||
|
@ -259,7 +259,7 @@
|
|||||||
static void postalias(char *map_type, char *path_name, int postalias_flags,
|
static void postalias(char *map_type, char *path_name, int postalias_flags,
|
||||||
int open_flags, int dict_flags)
|
int open_flags, int dict_flags)
|
||||||
{
|
{
|
||||||
VSTREAM *source_fp;
|
VSTREAM *NOCLOBBER source_fp;
|
||||||
VSTRING *line_buffer;
|
VSTRING *line_buffer;
|
||||||
MKMAP *mkmap;
|
MKMAP *mkmap;
|
||||||
int lineno;
|
int lineno;
|
||||||
@ -286,10 +286,10 @@ static void postalias(char *map_type, char *path_name, int postalias_flags,
|
|||||||
/* Create database. */
|
/* Create database. */
|
||||||
if (strcmp(map_type, DICT_TYPE_PROXY) == 0)
|
if (strcmp(map_type, DICT_TYPE_PROXY) == 0)
|
||||||
msg_fatal("can't create maps via the proxy service");
|
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)
|
if ((source_fp = vstream_fopen(path_name, O_RDONLY, 0)) == 0)
|
||||||
msg_fatal("open %s: %m", path_name);
|
msg_fatal("open %s: %m", path_name);
|
||||||
}
|
}
|
||||||
dict_flags |= DICT_FLAG_BULK_UPDATE;
|
|
||||||
if (fstat(vstream_fileno(source_fp), &st) < 0)
|
if (fstat(vstream_fileno(source_fp), &st) < 0)
|
||||||
msg_fatal("fstat %s: %m", path_name);
|
msg_fatal("fstat %s: %m", path_name);
|
||||||
|
|
||||||
@ -322,72 +322,85 @@ static void postalias(char *map_type, char *path_name, int postalias_flags,
|
|||||||
umask(saved_mask);
|
umask(saved_mask);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add records to the database.
|
* Trap "exceptions" so that we can restart a bulk-mode update after a
|
||||||
|
* recoverable error.
|
||||||
*/
|
*/
|
||||||
lineno = 0;
|
for (;;) {
|
||||||
while (readlline(line_buffer, source_fp, &lineno)) {
|
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));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Tokenize the input, so that we do the right thing when a quoted
|
* Add records to the database.
|
||||||
* localpart contains special characters such as "@", ":" and so on.
|
|
||||||
*/
|
*/
|
||||||
if ((tok_list = tok822_scan(STR(line_buffer), (TOK822 **) 0)) == 0)
|
lineno = 0;
|
||||||
continue;
|
while (readlline(line_buffer, source_fp, &lineno)) {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Enforce the key:value format. Disallow missing keys, multi-address
|
* Tokenize the input, so that we do the right thing when a
|
||||||
* keys, or missing values. In order to specify an empty string or
|
* quoted localpart contains special characters such as "@", ":"
|
||||||
* value, enclose it in double quotes.
|
* and so on.
|
||||||
*/
|
*/
|
||||||
if ((colon = tok822_find_type(tok_list, ':')) == 0
|
if ((tok_list = tok822_scan(STR(line_buffer), (TOK822 **) 0)) == 0)
|
||||||
|| colon->prev == 0 || colon->next == 0
|
continue;
|
||||||
|| tok822_rfind_type(colon, ',')) {
|
|
||||||
msg_warn("%s, line %d: need name:value pair",
|
/*
|
||||||
VSTREAM_PATH(source_fp), lineno);
|
* Enforce the key:value format. Disallow missing keys,
|
||||||
tok822_free_tree(tok_list);
|
* multi-address keys, or missing values. In order to specify an
|
||||||
continue;
|
* empty string or value, enclose it in double quotes.
|
||||||
|
*/
|
||||||
|
if ((colon = tok822_find_type(tok_list, ':')) == 0
|
||||||
|
|| colon->prev == 0 || colon->next == 0
|
||||||
|
|| tok822_rfind_type(colon, ',')) {
|
||||||
|
msg_warn("%s, line %d: need name:value pair",
|
||||||
|
VSTREAM_PATH(source_fp), lineno);
|
||||||
|
tok822_free_tree(tok_list);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
if (tok822_rfind_type(colon, '@') || tok822_rfind_type(colon, '%')) {
|
||||||
|
msg_warn("%s, line %d: name must be local",
|
||||||
|
VSTREAM_PATH(source_fp), lineno);
|
||||||
|
tok822_free_tree(tok_list);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
value_list = tok822_cut_after(colon);
|
||||||
|
tok822_unlink(colon);
|
||||||
|
tok822_free(colon);
|
||||||
|
|
||||||
|
tok822_internalize(key_buffer, key_list, TOK822_STR_DEFL);
|
||||||
|
tok822_free_tree(key_list);
|
||||||
|
|
||||||
|
tok822_externalize(value_buffer, value_list, TOK822_STR_DEFL);
|
||||||
|
tok822_free_tree(value_list);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Store the value under a case-insensitive key.
|
||||||
|
*/
|
||||||
|
mkmap_append(mkmap, STR(key_buffer), STR(value_buffer));
|
||||||
|
if (mkmap->dict->error)
|
||||||
|
msg_fatal("table %s:%s: write error: %m",
|
||||||
|
mkmap->dict->type, mkmap->dict->name);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
if (tok822_rfind_type(colon, '@') || tok822_rfind_type(colon, '%')) {
|
|
||||||
msg_warn("%s, line %d: name must be local",
|
|
||||||
VSTREAM_PATH(source_fp), lineno);
|
|
||||||
tok822_free_tree(tok_list);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
value_list = tok822_cut_after(colon);
|
|
||||||
tok822_unlink(colon);
|
|
||||||
tok822_free(colon);
|
|
||||||
|
|
||||||
tok822_internalize(key_buffer, key_list, TOK822_STR_DEFL);
|
|
||||||
tok822_free_tree(key_list);
|
|
||||||
|
|
||||||
tok822_externalize(value_buffer, value_list, TOK822_STR_DEFL);
|
|
||||||
tok822_free_tree(value_list);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Store the value under a case-insensitive key.
|
|
||||||
*/
|
|
||||||
mkmap_append(mkmap, STR(key_buffer), STR(value_buffer));
|
|
||||||
if (mkmap->dict->error)
|
|
||||||
msg_fatal("table %s:%s: write error: %m",
|
|
||||||
mkmap->dict->type, mkmap->dict->name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -328,7 +328,7 @@ typedef struct {
|
|||||||
static void postmap(char *map_type, char *path_name, int postmap_flags,
|
static void postmap(char *map_type, char *path_name, int postmap_flags,
|
||||||
int open_flags, int dict_flags)
|
int open_flags, int dict_flags)
|
||||||
{
|
{
|
||||||
VSTREAM *source_fp;
|
VSTREAM *NOCLOBBER source_fp;
|
||||||
VSTRING *line_buffer;
|
VSTRING *line_buffer;
|
||||||
MKMAP *mkmap;
|
MKMAP *mkmap;
|
||||||
int lineno;
|
int lineno;
|
||||||
@ -349,10 +349,10 @@ static void postmap(char *map_type, char *path_name, int postmap_flags,
|
|||||||
/* Create database. */
|
/* Create database. */
|
||||||
if (strcmp(map_type, DICT_TYPE_PROXY) == 0)
|
if (strcmp(map_type, DICT_TYPE_PROXY) == 0)
|
||||||
msg_fatal("can't create maps via the proxy service");
|
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)
|
if ((source_fp = vstream_fopen(path_name, O_RDONLY, 0)) == 0)
|
||||||
msg_fatal("open %s: %m", path_name);
|
msg_fatal("open %s: %m", path_name);
|
||||||
}
|
}
|
||||||
dict_flags |= DICT_FLAG_BULK_UPDATE;
|
|
||||||
if (fstat(vstream_fileno(source_fp), &st) < 0)
|
if (fstat(vstream_fileno(source_fp), &st) < 0)
|
||||||
msg_fatal("fstat %s: %m", path_name);
|
msg_fatal("fstat %s: %m", path_name);
|
||||||
|
|
||||||
@ -385,44 +385,56 @@ static void postmap(char *map_type, char *path_name, int postmap_flags,
|
|||||||
umask(saved_mask);
|
umask(saved_mask);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add records to the database.
|
* Trap "exceptions" so that we can restart a bulk-mode update after a
|
||||||
|
* recoverable error.
|
||||||
*/
|
*/
|
||||||
lineno = 0;
|
for (;;) {
|
||||||
while (readlline(line_buffer, source_fp, &lineno)) {
|
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));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Split on the first whitespace character, then trim leading and
|
* Add records to the database.
|
||||||
* trailing whitespace from key and value.
|
|
||||||
*/
|
*/
|
||||||
key = STR(line_buffer);
|
lineno = 0;
|
||||||
value = key + strcspn(key, " \t\r\n");
|
while (readlline(line_buffer, source_fp, &lineno)) {
|
||||||
if (*value)
|
|
||||||
*value++ = 0;
|
|
||||||
while (ISSPACE(*value))
|
|
||||||
value++;
|
|
||||||
trimblanks(key, 0)[0] = 0;
|
|
||||||
trimblanks(value, 0)[0] = 0;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Enforce the "key whitespace value" format. Disallow missing keys
|
* Split on the first whitespace character, then trim leading and
|
||||||
* or missing values.
|
* trailing whitespace from key and value.
|
||||||
*/
|
*/
|
||||||
if (*key == 0 || *value == 0) {
|
key = STR(line_buffer);
|
||||||
msg_warn("%s, line %d: expected format: key whitespace value",
|
value = key + strcspn(key, " \t\r\n");
|
||||||
VSTREAM_PATH(source_fp), lineno);
|
if (*value)
|
||||||
continue;
|
*value++ = 0;
|
||||||
|
while (ISSPACE(*value))
|
||||||
|
value++;
|
||||||
|
trimblanks(key, 0)[0] = 0;
|
||||||
|
trimblanks(value, 0)[0] = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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",
|
||||||
|
VSTREAM_PATH(source_fp), lineno);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (key[strlen(key) - 1] == ':')
|
||||||
|
msg_warn("%s, line %d: record is in \"key: value\" format; is this an alias file?",
|
||||||
|
VSTREAM_PATH(source_fp), lineno);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Store the value under a case-insensitive key.
|
||||||
|
*/
|
||||||
|
mkmap_append(mkmap, key, value);
|
||||||
|
if (mkmap->dict->error)
|
||||||
|
msg_fatal("table %s:%s: write error: %m",
|
||||||
|
mkmap->dict->type, mkmap->dict->name);
|
||||||
}
|
}
|
||||||
if (key[strlen(key) - 1] == ':')
|
break;
|
||||||
msg_warn("%s, line %d: record is in \"key: value\" format; is this an alias file?",
|
|
||||||
VSTREAM_PATH(source_fp), lineno);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Store the value under a case-insensitive key.
|
|
||||||
*/
|
|
||||||
mkmap_append(mkmap, key, value);
|
|
||||||
if (mkmap->dict->error)
|
|
||||||
msg_fatal("table %s:%s: write error: %m",
|
|
||||||
mkmap->dict->type, mkmap->dict->name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -16,8 +16,9 @@ CFLAGS = $(DEBUG) $(OPT) $(DEFS)
|
|||||||
TESTPROG=
|
TESTPROG=
|
||||||
PROG = postscreen
|
PROG = postscreen
|
||||||
INC_DIR = ../../include
|
INC_DIR = ../../include
|
||||||
LIBS = ../../lib/libmaster.a ../../lib/libtls.a ../../lib/libglobal.a \
|
# Fake libdns dependency, for early-binding shared-library builds.
|
||||||
../../lib/libutil.a
|
LIBS = ../../lib/libmaster.a ../../lib/libtls.a ../../lib/libdns.a \
|
||||||
|
../../lib/libglobal.a ../../lib/libutil.a
|
||||||
|
|
||||||
.c.o:; $(CC) $(CFLAGS) -c $*.c
|
.c.o:; $(CC) $(CFLAGS) -c $*.c
|
||||||
|
|
||||||
|
@ -170,7 +170,7 @@
|
|||||||
/* A list of local \fBpostscreen\fR(8) server IP addresses where a
|
/* A list of local \fBpostscreen\fR(8) server IP addresses where a
|
||||||
/* non-whitelisted remote SMTP client can obtain \fBpostscreen\fR(8)'s temporary
|
/* non-whitelisted remote SMTP client can obtain \fBpostscreen\fR(8)'s temporary
|
||||||
/* whitelist status.
|
/* whitelist status.
|
||||||
/* BEFORE-GREETING TESTS
|
/* BEFORE 220 GREETING TESTS
|
||||||
/* .ad
|
/* .ad
|
||||||
/* .fi
|
/* .fi
|
||||||
/* These tests are executed before the remote SMTP client
|
/* These tests are executed before the remote SMTP client
|
||||||
@ -220,15 +220,15 @@
|
|||||||
/* Allow a remote SMTP client to skip "before" and "after 220
|
/* Allow a remote SMTP client to skip "before" and "after 220
|
||||||
/* greeting" protocol tests, based on its combined DNSBL score as
|
/* greeting" protocol tests, based on its combined DNSBL score as
|
||||||
/* defined with the postscreen_dnsbl_sites parameter.
|
/* defined with the postscreen_dnsbl_sites parameter.
|
||||||
/* AFTER-GREETING TESTS
|
/* AFTER 220 GREETING TESTS
|
||||||
/* .ad
|
/* .ad
|
||||||
/* .fi
|
/* .fi
|
||||||
/* These tests are executed after the remote SMTP client
|
/* These tests are executed after the remote SMTP client
|
||||||
/* receives the "220 servername" greeting. If a client passes
|
/* receives the "220 servername" greeting. If a client passes
|
||||||
/* all tests during this phase, it will receive a 4XX response
|
/* all tests during this phase, it will receive a 4XX response
|
||||||
/* to RCPT TO commands until the client hangs up. After this,
|
/* to all RCPT TO commands. After the client reconnects, it
|
||||||
/* the client will be allowed to talk directly to a Postfix
|
/* will be allowed to talk directly to a Postfix SMTP server
|
||||||
/* SMTP server process.
|
/* process.
|
||||||
/* .IP "\fBpostscreen_bare_newline_action (ignore)\fR"
|
/* .IP "\fBpostscreen_bare_newline_action (ignore)\fR"
|
||||||
/* The action that \fBpostscreen\fR(8) takes when a remote SMTP client sends
|
/* 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
|
/* 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
|
TESTPROG= smtpd_token smtpd_check
|
||||||
PROG = smtpd
|
PROG = smtpd
|
||||||
INC_DIR = ../../include
|
INC_DIR = ../../include
|
||||||
|
# Fake libdns dependency, for early-binding shared-library builds.
|
||||||
LIBS = ../../lib/libmaster.a ../../lib/libtls.a ../../lib/libdns.a \
|
LIBS = ../../lib/libmaster.a ../../lib/libtls.a ../../lib/libdns.a \
|
||||||
../../lib/libxsasl.a ../../lib/libmilter.a ../../lib/libglobal.a \
|
../../lib/libxsasl.a ../../lib/libmilter.a ../../lib/libdns.a \
|
||||||
../../lib/libutil.a
|
../../lib/libglobal.a ../../lib/libutil.a
|
||||||
|
|
||||||
.c.o:; $(CC) $(CFLAGS) -c $*.c
|
.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 */
|
/* 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 RESOLVE_REPLY *reply;
|
||||||
const char *owners;
|
const char *owners;
|
||||||
@ -3470,6 +3470,9 @@ static int reject_auth_sender_login_mismatch(SMTPD_STATE *state, const char *sen
|
|||||||
char *name;
|
char *name;
|
||||||
int found = 0;
|
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.
|
* 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);
|
myfree(saved_owners);
|
||||||
}
|
} else if (allow_unknown_sender)
|
||||||
|
return (SMTPD_CHECK_DUNNO);
|
||||||
if (!found)
|
if (!found)
|
||||||
return (smtpd_check_reject(state, MAIL_ERROR_POLICY, 553, "5.7.1",
|
return (smtpd_check_reject(state, MAIL_ERROR_POLICY, 553, "5.7.1",
|
||||||
"<%s>: Sender address rejected: not owned by user %s",
|
"<%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
|
#ifdef USE_SASL_AUTH
|
||||||
if (var_smtpd_sasl_enable) {
|
if (var_smtpd_sasl_enable) {
|
||||||
if (state->sender && *state->sender)
|
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
|
} else
|
||||||
#endif
|
#endif
|
||||||
msg_warn("restriction `%s' ignored: no SASL support", name);
|
msg_warn("restriction `%s' ignored: no SASL support", name);
|
||||||
|
@ -8,8 +8,9 @@ CFLAGS = $(DEBUG) $(OPT) $(DEFS)
|
|||||||
TESTPROG=
|
TESTPROG=
|
||||||
PROG = tlsmgr
|
PROG = tlsmgr
|
||||||
INC_DIR = ../../include
|
INC_DIR = ../../include
|
||||||
LIBS = ../../lib/libmaster.a ../../lib/libtls.a ../../lib/libglobal.a \
|
# Fake libdns dependency, for early-binding shared-library builds.
|
||||||
../../lib/libutil.a
|
LIBS = ../../lib/libmaster.a ../../lib/libtls.a ../../lib/libdns.a \
|
||||||
|
../../lib/libglobal.a ../../lib/libutil.a
|
||||||
|
|
||||||
.c.o:; $(CC) $(CFLAGS) -c $*.c
|
.c.o:; $(CC) $(CFLAGS) -c $*.c
|
||||||
|
|
||||||
|
@ -1833,6 +1833,8 @@ stream_trigger.o: sys_defs.h
|
|||||||
stream_trigger.o: trigger.h
|
stream_trigger.o: trigger.h
|
||||||
sys_compat.o: sys_compat.c
|
sys_compat.o: sys_compat.c
|
||||||
sys_compat.o: sys_defs.h
|
sys_compat.o: sys_defs.h
|
||||||
|
timecmp.o: timecmp.c
|
||||||
|
timecmp.o: timecmp.h
|
||||||
timed_connect.o: iostuff.h
|
timed_connect.o: iostuff.h
|
||||||
timed_connect.o: msg.h
|
timed_connect.o: msg.h
|
||||||
timed_connect.o: sane_connect.h
|
timed_connect.o: sane_connect.h
|
||||||
|
@ -15,6 +15,13 @@
|
|||||||
* System library.
|
* System library.
|
||||||
*/
|
*/
|
||||||
#include <fcntl.h>
|
#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.
|
* Utility library.
|
||||||
@ -56,6 +63,7 @@ typedef struct DICT {
|
|||||||
VSTRING *fold_buf; /* key folding buffer */
|
VSTRING *fold_buf; /* key folding buffer */
|
||||||
DICT_OWNER owner; /* provenance */
|
DICT_OWNER owner; /* provenance */
|
||||||
int error; /* last operation only */
|
int error; /* last operation only */
|
||||||
|
DICT_JMP_BUF *jbuf; /* exception handling */
|
||||||
} DICT;
|
} DICT;
|
||||||
|
|
||||||
extern DICT *dict_alloc(const char *, const char *, ssize_t);
|
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"
|
#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
|
/* LICENSE
|
||||||
/* .ad
|
/* .ad
|
||||||
/* .fi
|
/* .fi
|
||||||
|
@ -13,6 +13,9 @@
|
|||||||
/*
|
/*
|
||||||
/* void dict_free(dict)
|
/* void dict_free(dict)
|
||||||
/* DICT *ptr;
|
/* DICT *ptr;
|
||||||
|
/*
|
||||||
|
/* void dict_jmp_alloc(dict)
|
||||||
|
/* DICT *ptr;
|
||||||
/* DESCRIPTION
|
/* DESCRIPTION
|
||||||
/* dict_alloc() allocates memory for a dictionary structure of
|
/* dict_alloc() allocates memory for a dictionary structure of
|
||||||
/* \fIsize\fR bytes, initializes all generic dictionary
|
/* \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
|
/* It is up to the caller to dispose of any memory that was allocated
|
||||||
/* by the caller.
|
/* by the caller.
|
||||||
/*
|
/*
|
||||||
|
/* dict_jmp_alloc() implements preliminary support for exception
|
||||||
|
/* handling. This will eventually be built into dict_alloc().
|
||||||
|
/*
|
||||||
/* Arguments:
|
/* Arguments:
|
||||||
/* .IP dict_type
|
/* .IP dict_type
|
||||||
/* The official name for this type of dictionary, as used by
|
/* The official name for this type of dictionary, as used by
|
||||||
@ -114,7 +120,7 @@ static int dict_default_lock(DICT *dict, int operation)
|
|||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* dict_default_close - trap unimplemented operation */
|
/* dict_default_close - trap unimplemented operation */
|
||||||
|
|
||||||
static void dict_default_close(DICT *dict)
|
static void dict_default_close(DICT *dict)
|
||||||
@ -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.status = DICT_OWNER_UNKNOWN;
|
||||||
dict->owner.uid = ~0;
|
dict->owner.uid = ~0;
|
||||||
dict->error = DICT_ERR_NONE;
|
dict->error = DICT_ERR_NONE;
|
||||||
|
dict->jbuf = 0;
|
||||||
return dict;
|
return dict;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,5 +161,20 @@ void dict_free(DICT *dict)
|
|||||||
{
|
{
|
||||||
myfree(dict->type);
|
myfree(dict->type);
|
||||||
myfree(dict->name);
|
myfree(dict->name);
|
||||||
|
if (dict->jbuf)
|
||||||
|
myfree((char *) dict->jbuf);
|
||||||
myfree((char *) dict);
|
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 open_flags;
|
||||||
/* int dict_flags;
|
/* int dict_flags;
|
||||||
/* DESCRIPTION
|
/* DESCRIPTION
|
||||||
/* dict_lmdb_open() opens the named LMDB database and makes it available
|
/* dict_lmdb_open() opens the named LMDB database and makes
|
||||||
/* via the generic interface described in dict_open(3).
|
/* it available via the generic interface described in
|
||||||
|
/* dict_open(3).
|
||||||
/*
|
/*
|
||||||
/* The dict_lmdb_map_size variable specifies a non-default
|
/* The dict_lmdb_map_size variable specifies the initial
|
||||||
/* per-table memory map size. The map size is also the maximum
|
/* database memory map size. When a map becomes full its size
|
||||||
/* size the table can grow to, so it must be set large enough
|
/* is doubled, and other programs pick up the size change.
|
||||||
/* 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.
|
|
||||||
/* DIAGNOSTICS
|
/* 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
|
/* SEE ALSO
|
||||||
/* dict(3) generic dictionary manager
|
/* dict(3) generic dictionary manager
|
||||||
/* LICENSE
|
/* LICENSE
|
||||||
@ -44,9 +37,14 @@
|
|||||||
/* AUTHOR(S)
|
/* AUTHOR(S)
|
||||||
/* Howard Chu
|
/* Howard Chu
|
||||||
/* Symas Corporation
|
/* 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
|
#ifdef HAS_LMDB
|
||||||
|
|
||||||
@ -61,20 +59,29 @@
|
|||||||
#include PATH_LMDB_H
|
#include PATH_LMDB_H
|
||||||
#else
|
#else
|
||||||
#include <lmdb.h>
|
#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
|
#endif
|
||||||
|
|
||||||
/* Utility library. */
|
/* Utility library. */
|
||||||
|
|
||||||
#include "msg.h"
|
#include <msg.h>
|
||||||
#include "mymalloc.h"
|
#include <mymalloc.h>
|
||||||
#include "htable.h"
|
#include <htable.h>
|
||||||
#include "iostuff.h"
|
#include <iostuff.h>
|
||||||
#include "vstring.h"
|
#include <vstring.h>
|
||||||
#include "myflock.h"
|
#include <myflock.h>
|
||||||
#include "stringops.h"
|
#include <stringops.h>
|
||||||
#include "dict.h"
|
#include <dict.h>
|
||||||
#include "dict_lmdb.h"
|
#include <dict_lmdb.h>
|
||||||
#include "warn_stat.h"
|
#include <warn_stat.h>
|
||||||
|
|
||||||
/* Application-specific. */
|
/* Application-specific. */
|
||||||
|
|
||||||
@ -84,55 +91,423 @@ typedef struct {
|
|||||||
MDB_dbi dbi; /* database handle */
|
MDB_dbi dbi; /* database handle */
|
||||||
MDB_txn *txn; /* bulk update transaction */
|
MDB_txn *txn; /* bulk update transaction */
|
||||||
MDB_cursor *cursor; /* for sequence ops */
|
MDB_cursor *cursor; /* for sequence ops */
|
||||||
|
size_t map_size; /* per-database size limit */
|
||||||
VSTRING *key_buf; /* key buffer */
|
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;
|
} 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) \
|
#define SCOPY(buf, data, size) \
|
||||||
vstring_str(vstring_strncpy(buf ? buf : (buf = vstring_alloc(10)), 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,
|
unsigned int dict_lmdb_max_readers = 216; /* 200 postfix processes,
|
||||||
* plus some extra */
|
* plus some extra */
|
||||||
|
|
||||||
|
/* #define msg_verbose 1 */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Out-of-space safety net. When an update or delete operation fails with
|
* The purpose of the error-recovering functions below is to hide LMDB
|
||||||
* MDB_MAP_FULL, extend the database file size so that the next
|
* quirks (MAP_FULL, MAP_CHANGED), so that the dict(3) API routines can
|
||||||
* dict_lmdb_open() call will force a 3x over-allocation.
|
* 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
|
* - To recover from a single-transaction LMDB error, each wrapper function
|
||||||
* operation. In private communication on August 18, 2013, Howard Chu
|
* uses tail recursion instead of goto. Since LMDB errors are rare, code
|
||||||
* confirmed this as follows: "It will have no effect. LMDB internally
|
* clarity is more important than speed.
|
||||||
* accounts for the last used page#, the filesystem's notion of filesize
|
|
||||||
* isn't used for any purpose."
|
|
||||||
*
|
*
|
||||||
* We make no assumptions about which LMDB operations may fail with
|
* - To recover from a bulk-mode transaction LMDB error, the error-recovery
|
||||||
* MDB_MAP_FULL. Instead we wrap all LMDB operations inside a Postfix
|
* code jumps back into the caller to some pre-arranged point (the closest
|
||||||
* function that may change a database.
|
* 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;
|
int status;
|
||||||
char *mdb_path = concatenate(dict->name, "." DICT_TYPE_LMDB, (char *) 0);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* After MDB_MAP_FULL error, expand the file size to trigger the 3x size
|
* This is called before accessing the database, or after recovery from
|
||||||
* limit workaround on the next open() attempt.
|
* 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
|
if (dict_lmdb->open_flags & O_TRUNC) {
|
||||||
&& truncate(mdb_path, dict_lmdb_map_size) < 0)
|
if ((status = mdb_drop(dict_lmdb->txn, dict_lmdb->dbi, 0)) != 0)
|
||||||
msg_warn("dict_lmdb_grow: cannot grow database file %s:%s: %m",
|
msg_fatal("truncate %s:%s: %s",
|
||||||
dict->type, dict->name);
|
dict_lmdb->dict.type, dict_lmdb->dict.name,
|
||||||
myfree(mdb_path);
|
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 */
|
/* dict_lmdb_lookup - find database entry */
|
||||||
|
|
||||||
static const char *dict_lmdb_lookup(DICT *dict, const char *name)
|
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;
|
DICT_LMDB *dict_lmdb = (DICT_LMDB *) dict;
|
||||||
MDB_val mdb_key;
|
MDB_val mdb_key;
|
||||||
MDB_val mdb_value;
|
MDB_val mdb_value;
|
||||||
MDB_txn *txn;
|
|
||||||
const char *result = 0;
|
const char *result = 0;
|
||||||
int status, klen;
|
int status, klen;
|
||||||
|
|
||||||
dict->error = 0;
|
dict->error = 0;
|
||||||
|
dict_lmdb->dict_api_retries = 0;
|
||||||
klen = strlen(name);
|
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));
|
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
|
* See if this LMDB file was written with one null byte appended to key
|
||||||
* and value.
|
* and value.
|
||||||
@ -178,10 +545,15 @@ static const char *dict_lmdb_lookup(DICT *dict, const char *name)
|
|||||||
if (dict->flags & DICT_FLAG_TRY1NULL) {
|
if (dict->flags & DICT_FLAG_TRY1NULL) {
|
||||||
mdb_key.mv_data = (void *) name;
|
mdb_key.mv_data = (void *) name;
|
||||||
mdb_key.mv_size = klen + 1;
|
mdb_key.mv_size = klen + 1;
|
||||||
status = mdb_get(txn, dict_lmdb->dbi, &mdb_key, &mdb_value);
|
status = dict_lmdb_get(dict_lmdb, &mdb_key, &mdb_value);
|
||||||
if (!status) {
|
if (status == 0) {
|
||||||
dict->flags &= ~DICT_FLAG_TRY0NULL;
|
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)) {
|
if (result == 0 && (dict->flags & DICT_FLAG_TRY0NULL)) {
|
||||||
mdb_key.mv_data = (void *) name;
|
mdb_key.mv_data = (void *) name;
|
||||||
mdb_key.mv_size = klen;
|
mdb_key.mv_size = klen;
|
||||||
status = mdb_get(txn, dict_lmdb->dbi, &mdb_key, &mdb_value);
|
status = dict_lmdb_get(dict_lmdb, &mdb_key, &mdb_value);
|
||||||
if (!status) {
|
if (status == 0) {
|
||||||
dict->flags &= ~DICT_FLAG_TRY1NULL;
|
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);
|
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;
|
DICT_LMDB *dict_lmdb = (DICT_LMDB *) dict;
|
||||||
MDB_val mdb_key;
|
MDB_val mdb_key;
|
||||||
MDB_val mdb_value;
|
MDB_val mdb_value;
|
||||||
MDB_txn *txn;
|
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
dict->error = 0;
|
dict->error = 0;
|
||||||
|
dict_lmdb->dict_api_retries = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sanity check.
|
* 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));
|
name = lowercase(vstring_str(dict->fold_buf));
|
||||||
}
|
}
|
||||||
mdb_key.mv_data = (void *) name;
|
mdb_key.mv_data = (void *) name;
|
||||||
|
|
||||||
mdb_value.mv_data = (void *) value;
|
mdb_value.mv_data = (void *) value;
|
||||||
mdb_key.mv_size = strlen(name);
|
mdb_key.mv_size = strlen(name);
|
||||||
mdb_value.mv_size = strlen(value);
|
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++;
|
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.
|
* Do the update.
|
||||||
*/
|
*/
|
||||||
(void) DICT_LMDB_WRAPPER(dict, status,
|
status = dict_lmdb_put(dict_lmdb, &mdb_key, &mdb_value,
|
||||||
mdb_put(txn, dict_lmdb->dbi, &mdb_key, &mdb_value,
|
(dict->flags & DICT_FLAG_DUP_REPLACE) ? 0 : MDB_NOOVERWRITE);
|
||||||
(dict->flags & DICT_FLAG_DUP_REPLACE) ? 0 : MDB_NOOVERWRITE));
|
if (status != 0) {
|
||||||
if (status) {
|
|
||||||
if (status == MDB_KEYEXIST) {
|
if (status == MDB_KEYEXIST) {
|
||||||
if (dict->flags & DICT_FLAG_DUP_IGNORE)
|
if (dict->flags & DICT_FLAG_DUP_IGNORE)
|
||||||
/* void */ ;
|
/* void */ ;
|
||||||
else if (dict->flags & DICT_FLAG_DUP_WARN)
|
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
|
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 {
|
} 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);
|
return (status);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -304,10 +662,10 @@ static int dict_lmdb_delete(DICT *dict, const char *name)
|
|||||||
{
|
{
|
||||||
DICT_LMDB *dict_lmdb = (DICT_LMDB *) dict;
|
DICT_LMDB *dict_lmdb = (DICT_LMDB *) dict;
|
||||||
MDB_val mdb_key;
|
MDB_val mdb_key;
|
||||||
MDB_txn *txn;
|
int status = 1, klen;
|
||||||
int status = 1, klen, rc;
|
|
||||||
|
|
||||||
dict->error = 0;
|
dict->error = 0;
|
||||||
|
dict_lmdb->dict_api_retries = 0;
|
||||||
klen = strlen(name);
|
klen = strlen(name);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -326,15 +684,6 @@ static int dict_lmdb_delete(DICT *dict, const char *name)
|
|||||||
name = lowercase(vstring_str(dict->fold_buf));
|
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
|
* See if this LMDB file was written with one null byte appended to key
|
||||||
* and value.
|
* and value.
|
||||||
@ -342,13 +691,14 @@ static int dict_lmdb_delete(DICT *dict, const char *name)
|
|||||||
if (dict->flags & DICT_FLAG_TRY1NULL) {
|
if (dict->flags & DICT_FLAG_TRY1NULL) {
|
||||||
mdb_key.mv_data = (void *) name;
|
mdb_key.mv_data = (void *) name;
|
||||||
mdb_key.mv_size = klen + 1;
|
mdb_key.mv_size = klen + 1;
|
||||||
(void) DICT_LMDB_WRAPPER(dict, status,
|
status = dict_lmdb_del(dict_lmdb, &mdb_key);
|
||||||
mdb_del(txn, dict_lmdb->dbi, &mdb_key, NULL));
|
if (status != 0) {
|
||||||
if (status) {
|
|
||||||
if (status == MDB_NOTFOUND)
|
if (status == MDB_NOTFOUND)
|
||||||
status = 1;
|
status = 1;
|
||||||
else
|
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 {
|
} else {
|
||||||
dict->flags &= ~DICT_FLAG_TRY0NULL; /* found */
|
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)) {
|
if (status > 0 && (dict->flags & DICT_FLAG_TRY0NULL)) {
|
||||||
mdb_key.mv_data = (void *) name;
|
mdb_key.mv_data = (void *) name;
|
||||||
mdb_key.mv_size = klen;
|
mdb_key.mv_size = klen;
|
||||||
(void) DICT_LMDB_WRAPPER(dict, status,
|
status = dict_lmdb_del(dict_lmdb, &mdb_key);
|
||||||
mdb_del(txn, dict_lmdb->dbi, &mdb_key, NULL));
|
if (status != 0) {
|
||||||
if (status) {
|
|
||||||
if (status == MDB_NOTFOUND)
|
if (status == MDB_NOTFOUND)
|
||||||
status = 1;
|
status = 1;
|
||||||
else
|
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 {
|
} else {
|
||||||
dict->flags &= ~DICT_FLAG_TRY1NULL; /* found */
|
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);
|
return (status);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* traverse the dictionary */
|
/* dict_lmdb_sequence - traverse the dictionary */
|
||||||
|
|
||||||
static int dict_lmdb_sequence(DICT *dict, int function,
|
static int dict_lmdb_sequence(DICT *dict, int function,
|
||||||
const char **key, const char **value)
|
const char **key, const char **value)
|
||||||
@ -396,6 +740,7 @@ static int dict_lmdb_sequence(DICT *dict, int function,
|
|||||||
int status;
|
int status;
|
||||||
|
|
||||||
dict->error = 0;
|
dict->error = 0;
|
||||||
|
dict_lmdb->dict_api_retries = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Determine the seek function.
|
* 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);
|
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.
|
* Database lookup.
|
||||||
*/
|
*/
|
||||||
status = mdb_cursor_get(dict_lmdb->cursor, &mdb_key, &mdb_value, op);
|
status = dict_lmdb_cursor_get(dict_lmdb, &mdb_key, &mdb_value, op);
|
||||||
if (status && status != MDB_NOTFOUND)
|
|
||||||
msg_fatal("%s: seeking dictionary: %s", dict_lmdb->dict.name, mdb_strerror(status));
|
|
||||||
|
|
||||||
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;
|
status = 1;
|
||||||
txn = mdb_cursor_txn(dict_lmdb->cursor);
|
txn = mdb_cursor_txn(dict_lmdb->cursor);
|
||||||
mdb_cursor_close(dict_lmdb->cursor);
|
mdb_cursor_close(dict_lmdb->cursor);
|
||||||
mdb_txn_abort(txn);
|
mdb_txn_abort(txn);
|
||||||
dict_lmdb->cursor = 0;
|
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);
|
default:
|
||||||
|
msg_fatal("error seeking %s:%s: %s",
|
||||||
if (mdb_value.mv_data != 0 && mdb_value.mv_size > 0) {
|
dict_lmdb->dict.type, dict_lmdb->dict.name,
|
||||||
|
mdb_strerror(status));
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (status);
|
return (status);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -472,20 +809,8 @@ static void dict_lmdb_close(DICT *dict)
|
|||||||
{
|
{
|
||||||
DICT_LMDB *dict_lmdb = (DICT_LMDB *) dict;
|
DICT_LMDB *dict_lmdb = (DICT_LMDB *) dict;
|
||||||
|
|
||||||
if (dict_lmdb->txn) {
|
dict_lmdb->dict_api_retries = 0;
|
||||||
int status;
|
dict_lmdb_finish(dict_lmdb);
|
||||||
|
|
||||||
(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);
|
|
||||||
}
|
|
||||||
if (dict_lmdb->dict.stat_fd >= 0)
|
if (dict_lmdb->dict.stat_fd >= 0)
|
||||||
close(dict_lmdb->dict.stat_fd);
|
close(dict_lmdb->dict.stat_fd);
|
||||||
mdb_env_close(dict_lmdb->env);
|
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;
|
MDB_dbi dbi;
|
||||||
char *mdb_path;
|
char *mdb_path;
|
||||||
int env_flags, status;
|
int env_flags, status;
|
||||||
|
size_t map_size = dict_lmdb_map_size;
|
||||||
|
|
||||||
mdb_path = concatenate(path, "." DICT_TYPE_LMDB, (char *) 0);
|
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)))
|
if ((status = mdb_env_create(&env)))
|
||||||
msg_fatal("env_create %s: %s", mdb_path, mdb_strerror(status));
|
msg_fatal("env_create %s: %s", mdb_path, mdb_strerror(status));
|
||||||
|
|
||||||
/*
|
if (stat(mdb_path, &st) == 0 && st.st_size > map_size)
|
||||||
* Try to ensure that the LMDB size limit is at least 3x the current LMDB
|
map_size = st.st_size;
|
||||||
* file size. This ensures that Postfix daemon processes can recover from
|
if ((status = mdb_env_set_mapsize(env, map_size)))
|
||||||
* 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)))
|
|
||||||
msg_fatal("env_set_mapsize %s: %s", mdb_path, mdb_strerror(status));
|
msg_fatal("env_set_mapsize %s: %s", mdb_path, mdb_strerror(status));
|
||||||
|
|
||||||
if ((status = mdb_env_set_maxreaders(env, dict_lmdb_max_readers)))
|
if ((status = mdb_env_set_maxreaders(env, dict_lmdb_max_readers)))
|
||||||
msg_fatal("env_set_maxreaders %s: %s", mdb_path, mdb_strerror(status));
|
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)))
|
if ((status = mdb_txn_begin(env, NULL, env_flags & MDB_RDONLY, &txn)))
|
||||||
msg_fatal("txn_begin %s: %s", mdb_path, mdb_strerror(status));
|
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
|
* mdb_open() requires a txn, but since the default DB always exists in
|
||||||
* LMDB environment, we usually don't need to do anything else with the
|
* an LMDB environment, we usually don't need to do anything else with
|
||||||
* txn.
|
* the txn. It is currently used for bulk transactions.
|
||||||
*/
|
*/
|
||||||
if ((status = mdb_open(txn, NULL, 0, &dbi)))
|
if ((status = mdb_open(txn, NULL, 0, &dbi)))
|
||||||
msg_fatal("mdb_open %s: %s", mdb_path, mdb_strerror(status));
|
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_LMDB *) dict_alloc(DICT_TYPE_LMDB, path, sizeof(*dict_lmdb));
|
||||||
dict_lmdb->dict.lookup = dict_lmdb_lookup;
|
dict_lmdb->dict.lookup = dict_lmdb_lookup;
|
||||||
dict_lmdb->dict.update = dict_lmdb_update;
|
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->dict.fold_buf = vstring_alloc(10);
|
||||||
dict_lmdb->env = env;
|
dict_lmdb->env = env;
|
||||||
dict_lmdb->dbi = dbi;
|
dict_lmdb->dbi = dbi;
|
||||||
|
dict_lmdb->map_size = map_size;
|
||||||
/* Save the write txn if we opened with O_TRUNC */
|
|
||||||
dict_lmdb->txn = txn;
|
|
||||||
|
|
||||||
dict_lmdb->cursor = 0;
|
dict_lmdb->cursor = 0;
|
||||||
dict_lmdb->key_buf = 0;
|
dict_lmdb->key_buf = 0;
|
||||||
dict_lmdb->val_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);
|
myfree(mdb_path);
|
||||||
|
|
||||||
return (DICT_DEBUG (&dict_lmdb->dict));
|
return (DICT_DEBUG (&dict_lmdb->dict));
|
||||||
|
@ -44,6 +44,16 @@
|
|||||||
/* DICT *(*open) (const char *, int, int);
|
/* DICT *(*open) (const char *, int, int);
|
||||||
/*
|
/*
|
||||||
/* ARGV *dict_mapnames()
|
/* 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
|
/* DESCRIPTION
|
||||||
/* This module implements a low-level interface to multiple
|
/* This module implements a low-level interface to multiple
|
||||||
/* physical dictionary types.
|
/* physical dictionary types.
|
||||||
@ -112,6 +122,10 @@
|
|||||||
/* .IP DICT_FLAG_PARANOID
|
/* .IP DICT_FLAG_PARANOID
|
||||||
/* A combination of all the paranoia flags: DICT_FLAG_NO_REGSUB,
|
/* A combination of all the paranoia flags: DICT_FLAG_NO_REGSUB,
|
||||||
/* DICT_FLAG_NO_PROXY and DICT_FLAG_NO_UNAUTH.
|
/* 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
|
/* .IP DICT_FLAG_DEBUG
|
||||||
/* Enable additional logging.
|
/* Enable additional logging.
|
||||||
/* .PP
|
/* .PP
|
||||||
@ -169,6 +183,18 @@
|
|||||||
/*
|
/*
|
||||||
/* dict_mapnames() returns a sorted list with the names of all available
|
/* dict_mapnames() returns a sorted list with the names of all available
|
||||||
/* dictionary types.
|
/* 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
|
/* DIAGNOSTICS
|
||||||
/* Fatal error: open error, unsupported dictionary type, attempt to
|
/* Fatal error: open error, unsupported dictionary type, attempt to
|
||||||
/* update non-writable dictionary.
|
/* update non-writable dictionary.
|
||||||
|
@ -45,6 +45,8 @@ void dict_test(int argc, char **argv)
|
|||||||
int n;
|
int n;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
#define USAGE "verbose|del key|get key|put key=value|first|next|masks|flags"
|
||||||
|
|
||||||
signal(SIGPIPE, SIG_IGN);
|
signal(SIGPIPE, SIG_IGN);
|
||||||
|
|
||||||
msg_vstream_init(argv[0], VSTREAM_ERR);
|
msg_vstream_init(argv[0], VSTREAM_ERR);
|
||||||
@ -88,7 +90,7 @@ void dict_test(int argc, char **argv)
|
|||||||
if (*bufp == '#')
|
if (*bufp == '#')
|
||||||
continue;
|
continue;
|
||||||
if ((cmd = mystrtok(&bufp, " ")) == 0) {
|
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);
|
vstream_fflush(VSTREAM_OUT);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -143,7 +145,7 @@ void dict_test(int argc, char **argv)
|
|||||||
vstream_printf("DICT_FLAG_INST_MASK %s\n",
|
vstream_printf("DICT_FLAG_INST_MASK %s\n",
|
||||||
dict_flags_str(DICT_FLAG_INST_MASK));
|
dict_flags_str(DICT_FLAG_INST_MASK));
|
||||||
} else {
|
} 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);
|
vstream_fflush(VSTREAM_OUT);
|
||||||
}
|
}
|
||||||
|
@ -46,15 +46,15 @@
|
|||||||
#define HAS_FSYNC
|
#define HAS_FSYNC
|
||||||
#define HAS_DB
|
#define HAS_DB
|
||||||
#define HAS_SA_LEN
|
#define HAS_SA_LEN
|
||||||
#define DEF_DB_TYPE "hash"
|
#define NATIVE_DB_TYPE "hash"
|
||||||
#if (defined(__NetBSD_Version__) && __NetBSD_Version__ >= 104250000)
|
#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
|
#endif
|
||||||
#if (defined(OpenBSD) && OpenBSD >= 200006)
|
#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
|
#endif
|
||||||
#ifndef ALIAS_DB_MAP
|
#ifndef ALIAS_DB_MAP
|
||||||
#define ALIAS_DB_MAP "hash:/etc/aliases"
|
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/aliases"
|
||||||
#endif
|
#endif
|
||||||
#define GETTIMEOFDAY(t) gettimeofday(t,(struct timezone *) 0)
|
#define GETTIMEOFDAY(t) gettimeofday(t,(struct timezone *) 0)
|
||||||
#define ROOT_PATH "/bin:/usr/bin:/sbin:/usr/sbin"
|
#define ROOT_PATH "/bin:/usr/bin:/sbin:/usr/sbin"
|
||||||
@ -227,8 +227,8 @@
|
|||||||
#define HAS_FSYNC
|
#define HAS_FSYNC
|
||||||
#define HAS_DB
|
#define HAS_DB
|
||||||
#define HAS_SA_LEN
|
#define HAS_SA_LEN
|
||||||
#define DEF_DB_TYPE "hash"
|
#define NATIVE_DB_TYPE "hash"
|
||||||
#define ALIAS_DB_MAP "hash:/etc/aliases"
|
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/aliases"
|
||||||
#define GETTIMEOFDAY(t) gettimeofday(t,(struct timezone *) 0)
|
#define GETTIMEOFDAY(t) gettimeofday(t,(struct timezone *) 0)
|
||||||
#define ROOT_PATH "/bin:/usr/bin:/sbin:/usr/sbin"
|
#define ROOT_PATH "/bin:/usr/bin:/sbin:/usr/sbin"
|
||||||
#define USE_STATFS
|
#define USE_STATFS
|
||||||
@ -287,12 +287,12 @@
|
|||||||
#define HAS_FSYNC
|
#define HAS_FSYNC
|
||||||
/* might be set by makedef */
|
/* might be set by makedef */
|
||||||
#ifdef HAS_DB
|
#ifdef HAS_DB
|
||||||
#define DEF_DB_TYPE "hash"
|
#define NATIVE_DB_TYPE "hash"
|
||||||
#define ALIAS_DB_MAP "hash:/etc/aliases"
|
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/aliases"
|
||||||
#else
|
#else
|
||||||
#define HAS_DBM
|
#define HAS_DBM
|
||||||
#define DEF_DB_TYPE "dbm"
|
#define NATIVE_DB_TYPE "dbm"
|
||||||
#define ALIAS_DB_MAP "dbm:/etc/aliases"
|
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/aliases"
|
||||||
#endif
|
#endif
|
||||||
extern int optind;
|
extern int optind;
|
||||||
extern char *optarg;
|
extern char *optarg;
|
||||||
@ -336,8 +336,8 @@ extern int h_errno;
|
|||||||
#define HAS_FSYNC
|
#define HAS_FSYNC
|
||||||
#define HAVE_BASENAME
|
#define HAVE_BASENAME
|
||||||
#define HAS_DBM
|
#define HAS_DBM
|
||||||
#define DEF_DB_TYPE "dbm"
|
#define NATIVE_DB_TYPE "dbm"
|
||||||
#define ALIAS_DB_MAP "dbm:/var/adm/sendmail/aliases"
|
#define ALIAS_DB_MAP DEF_DB_TYPE ":/var/adm/sendmail/aliases"
|
||||||
extern int optind; /* XXX use <getopt.h> */
|
extern int optind; /* XXX use <getopt.h> */
|
||||||
extern char *optarg; /* XXX use <getopt.h> */
|
extern char *optarg; /* XXX use <getopt.h> */
|
||||||
extern int opterr; /* 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 DEF_MAILBOX_LOCK "flock, dotlock"
|
||||||
#define HAS_FSYNC
|
#define HAS_FSYNC
|
||||||
#define HAS_DBM
|
#define HAS_DBM
|
||||||
#define DEF_DB_TYPE "dbm"
|
#define NATIVE_DB_TYPE "dbm"
|
||||||
#define ALIAS_DB_MAP "dbm:/etc/aliases"
|
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/aliases"
|
||||||
extern int optind;
|
extern int optind;
|
||||||
extern char *optarg;
|
extern char *optarg;
|
||||||
extern int opterr;
|
extern int opterr;
|
||||||
@ -429,8 +429,8 @@ extern int opterr;
|
|||||||
#define DEF_MAILBOX_LOCK "fcntl, dotlock"
|
#define DEF_MAILBOX_LOCK "fcntl, dotlock"
|
||||||
#define HAS_FSYNC
|
#define HAS_FSYNC
|
||||||
#define HAS_DBM
|
#define HAS_DBM
|
||||||
#define DEF_DB_TYPE "dbm"
|
#define NATIVE_DB_TYPE "dbm"
|
||||||
#define ALIAS_DB_MAP "dbm:/etc/mail/aliases"
|
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/mail/aliases"
|
||||||
#ifndef NO_NIS
|
#ifndef NO_NIS
|
||||||
#define HAS_NIS
|
#define HAS_NIS
|
||||||
#define HAS_NISPLUS
|
#define HAS_NISPLUS
|
||||||
@ -506,8 +506,8 @@ extern int opterr;
|
|||||||
#define DEF_MAILBOX_LOCK "fcntl, dotlock"
|
#define DEF_MAILBOX_LOCK "fcntl, dotlock"
|
||||||
#define HAS_FSYNC
|
#define HAS_FSYNC
|
||||||
#define HAS_DBM
|
#define HAS_DBM
|
||||||
#define DEF_DB_TYPE "dbm"
|
#define NATIVE_DB_TYPE "dbm"
|
||||||
#define ALIAS_DB_MAP "dbm:/etc/mail/aliases"
|
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/mail/aliases"
|
||||||
#ifndef NO_NIS
|
#ifndef NO_NIS
|
||||||
#define HAS_NIS
|
#define HAS_NIS
|
||||||
#endif
|
#endif
|
||||||
@ -537,8 +537,8 @@ extern int opterr;
|
|||||||
#define DEF_MAILBOX_LOCK "fcntl, dotlock"
|
#define DEF_MAILBOX_LOCK "fcntl, dotlock"
|
||||||
#define HAS_FSYNC
|
#define HAS_FSYNC
|
||||||
#define HAS_DBM
|
#define HAS_DBM
|
||||||
#define DEF_DB_TYPE "dbm"
|
#define NATIVE_DB_TYPE "dbm"
|
||||||
#define ALIAS_DB_MAP "dbm:/etc/mail/aliases"
|
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/mail/aliases"
|
||||||
#ifndef NO_NIS
|
#ifndef NO_NIS
|
||||||
#define HAS_NIS */
|
#define HAS_NIS */
|
||||||
#endif
|
#endif
|
||||||
@ -580,8 +580,8 @@ extern int opterr;
|
|||||||
#define USE_SYS_SELECT_H
|
#define USE_SYS_SELECT_H
|
||||||
#define HAS_FSYNC
|
#define HAS_FSYNC
|
||||||
#define HAS_DBM
|
#define HAS_DBM
|
||||||
#define DEF_DB_TYPE "dbm"
|
#define NATIVE_DB_TYPE "dbm"
|
||||||
#define ALIAS_DB_MAP "dbm:/etc/aliases"
|
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/aliases"
|
||||||
#ifndef NO_NIS
|
#ifndef NO_NIS
|
||||||
#define HAS_NIS
|
#define HAS_NIS
|
||||||
#endif
|
#endif
|
||||||
@ -634,8 +634,8 @@ extern int opterr;
|
|||||||
#define USE_SYS_SELECT_H
|
#define USE_SYS_SELECT_H
|
||||||
#define HAS_FSYNC
|
#define HAS_FSYNC
|
||||||
#define HAS_DBM
|
#define HAS_DBM
|
||||||
#define DEF_DB_TYPE "dbm"
|
#define NATIVE_DB_TYPE "dbm"
|
||||||
#define ALIAS_DB_MAP "dbm:/etc/aliases"
|
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/aliases"
|
||||||
#ifndef NO_NIS
|
#ifndef NO_NIS
|
||||||
#define HAS_NIS
|
#define HAS_NIS
|
||||||
#endif
|
#endif
|
||||||
@ -680,8 +680,8 @@ extern int initgroups(const char *, int);
|
|||||||
#define USE_SYS_SELECT_H
|
#define USE_SYS_SELECT_H
|
||||||
#define HAS_FSYNC
|
#define HAS_FSYNC
|
||||||
#define HAS_DBM
|
#define HAS_DBM
|
||||||
#define DEF_DB_TYPE "dbm"
|
#define NATIVE_DB_TYPE "dbm"
|
||||||
#define ALIAS_DB_MAP "dbm:/etc/aliases"
|
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/aliases"
|
||||||
#ifndef NO_NIS
|
#ifndef NO_NIS
|
||||||
#define HAS_NIS
|
#define HAS_NIS
|
||||||
#endif
|
#endif
|
||||||
@ -722,8 +722,8 @@ extern int initgroups(const char *, int);
|
|||||||
#define DEF_MAILBOX_LOCK "fcntl, dotlock"
|
#define DEF_MAILBOX_LOCK "fcntl, dotlock"
|
||||||
#define HAS_FSYNC
|
#define HAS_FSYNC
|
||||||
#define HAS_DBM
|
#define HAS_DBM
|
||||||
#define DEF_DB_TYPE "dbm"
|
#define NATIVE_DB_TYPE "dbm"
|
||||||
#define ALIAS_DB_MAP "dbm:/etc/aliases"
|
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/aliases"
|
||||||
#ifndef NO_NIS
|
#ifndef NO_NIS
|
||||||
#define HAS_NIS
|
#define HAS_NIS
|
||||||
#endif
|
#endif
|
||||||
@ -766,8 +766,8 @@ extern int initgroups(const char *, int);
|
|||||||
#define DEF_MAILBOX_LOCK "fcntl, dotlock" /* RedHat >= 4.x */
|
#define DEF_MAILBOX_LOCK "fcntl, dotlock" /* RedHat >= 4.x */
|
||||||
#define HAS_FSYNC
|
#define HAS_FSYNC
|
||||||
#define HAS_DB
|
#define HAS_DB
|
||||||
#define DEF_DB_TYPE "hash"
|
#define NATIVE_DB_TYPE "hash"
|
||||||
#define ALIAS_DB_MAP "hash:/etc/aliases"
|
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/aliases"
|
||||||
#ifndef NO_NIS
|
#ifndef NO_NIS
|
||||||
#define HAS_NIS
|
#define HAS_NIS
|
||||||
#endif
|
#endif
|
||||||
@ -843,8 +843,8 @@ extern int initgroups(const char *, int);
|
|||||||
#define DEF_MAILBOX_LOCK "dotlock" /* verified RedHat 3.03 */
|
#define DEF_MAILBOX_LOCK "dotlock" /* verified RedHat 3.03 */
|
||||||
#define HAS_FSYNC
|
#define HAS_FSYNC
|
||||||
#define HAS_DB
|
#define HAS_DB
|
||||||
#define DEF_DB_TYPE "hash"
|
#define NATIVE_DB_TYPE "hash"
|
||||||
#define ALIAS_DB_MAP "hash:/etc/aliases"
|
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/aliases"
|
||||||
#ifndef NO_NIS
|
#ifndef NO_NIS
|
||||||
#define HAS_NIS
|
#define HAS_NIS
|
||||||
#endif
|
#endif
|
||||||
@ -877,8 +877,8 @@ extern int initgroups(const char *, int);
|
|||||||
#define DEF_MAILBOX_LOCK "fcntl, dotlock" /* RedHat >= 4.x */
|
#define DEF_MAILBOX_LOCK "fcntl, dotlock" /* RedHat >= 4.x */
|
||||||
#define HAS_FSYNC
|
#define HAS_FSYNC
|
||||||
#define HAS_DB
|
#define HAS_DB
|
||||||
#define DEF_DB_TYPE "hash"
|
#define NATIVE_DB_TYPE "hash"
|
||||||
#define ALIAS_DB_MAP "hash:/etc/aliases"
|
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/aliases"
|
||||||
#ifndef NO_NIS
|
#ifndef NO_NIS
|
||||||
#define HAS_NIS
|
#define HAS_NIS
|
||||||
#endif
|
#endif
|
||||||
@ -943,8 +943,8 @@ extern int initgroups(const char *, int);
|
|||||||
#define INTERNAL_LOCK MYFLOCK_STYLE_FCNTL
|
#define INTERNAL_LOCK MYFLOCK_STYLE_FCNTL
|
||||||
#define DEF_MAILBOX_LOCK "fcntl, dotlock"
|
#define DEF_MAILBOX_LOCK "fcntl, dotlock"
|
||||||
#define HAS_FSYNC
|
#define HAS_FSYNC
|
||||||
#define DEF_DB_TYPE "dbm"
|
#define NATIVE_DB_TYPE "dbm"
|
||||||
#define ALIAS_DB_MAP "dbm:/etc/mail/aliases"
|
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/mail/aliases"
|
||||||
#define ROOT_PATH "/usr/bin:/sbin:/usr/sbin"
|
#define ROOT_PATH "/usr/bin:/sbin:/usr/sbin"
|
||||||
#define MISSING_SETENV
|
#define MISSING_SETENV
|
||||||
#ifndef NO_NIS
|
#ifndef NO_NIS
|
||||||
@ -982,8 +982,8 @@ extern int h_errno; /* <netdb.h> imports too much stuff */
|
|||||||
#define INTERNAL_LOCK MYFLOCK_STYLE_FCNTL
|
#define INTERNAL_LOCK MYFLOCK_STYLE_FCNTL
|
||||||
#define DEF_MAILBOX_LOCK "fcntl, dotlock"
|
#define DEF_MAILBOX_LOCK "fcntl, dotlock"
|
||||||
#define HAS_FSYNC
|
#define HAS_FSYNC
|
||||||
#define DEF_DB_TYPE "dbm"
|
#define NATIVE_DB_TYPE "dbm"
|
||||||
#define ALIAS_DB_MAP "dbm:/etc/mail/aliases"
|
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/mail/aliases"
|
||||||
#define ROOT_PATH "/usr/bin:/sbin:/usr/sbin"
|
#define ROOT_PATH "/usr/bin:/sbin:/usr/sbin"
|
||||||
#define MISSING_SETENV
|
#define MISSING_SETENV
|
||||||
#ifndef NO_NIS
|
#ifndef NO_NIS
|
||||||
@ -1027,8 +1027,8 @@ extern int h_errno; /* <netdb.h> imports too much stuff */
|
|||||||
#define MISSING_SETENV
|
#define MISSING_SETENV
|
||||||
#define MISSING_RLIMIT_FSIZE
|
#define MISSING_RLIMIT_FSIZE
|
||||||
#define GETTIMEOFDAY(t) gettimeofday(t,(struct timezone *) 0)
|
#define GETTIMEOFDAY(t) gettimeofday(t,(struct timezone *) 0)
|
||||||
#define DEF_DB_TYPE "dbm"
|
#define NATIVE_DB_TYPE "dbm"
|
||||||
#define ALIAS_DB_MAP "dbm:/usr/lib/aliases"
|
#define ALIAS_DB_MAP DEF_DB_TYPE ":/usr/lib/aliases"
|
||||||
#define ROOT_PATH "/bin:/usr/bin:/etc"
|
#define ROOT_PATH "/bin:/usr/bin:/etc"
|
||||||
#define _PATH_BSHELL "/bin/sh"
|
#define _PATH_BSHELL "/bin/sh"
|
||||||
#define _PATH_MAILDIR "/usr/mail"
|
#define _PATH_MAILDIR "/usr/mail"
|
||||||
@ -1087,7 +1087,7 @@ extern int h_errno;
|
|||||||
#define _PATH_DEFPATH "/bin:/usr/bin:/usr/ucb"
|
#define _PATH_DEFPATH "/bin:/usr/bin:/usr/ucb"
|
||||||
#define _PATH_STDPATH "/bin:/usr/bin:/usr/ucb"
|
#define _PATH_STDPATH "/bin:/usr/bin:/usr/ucb"
|
||||||
#define ROOT_PATH "/bin:/usr/bin:/usr/etc:/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"
|
#define ALIAS_DB_MAP "netinfo:/aliases"
|
||||||
#include <libc.h>
|
#include <libc.h>
|
||||||
#define MISSING_POSIX_S_IS
|
#define MISSING_POSIX_S_IS
|
||||||
@ -1142,7 +1142,7 @@ typedef unsigned short mode_t;
|
|||||||
#define _PATH_DEFPATH "/bin:/usr/bin:/usr/ucb"
|
#define _PATH_DEFPATH "/bin:/usr/bin:/usr/ucb"
|
||||||
#define _PATH_STDPATH "/bin:/usr/bin:/usr/ucb"
|
#define _PATH_STDPATH "/bin:/usr/bin:/usr/ucb"
|
||||||
#define ROOT_PATH "/bin:/usr/bin:/usr/etc:/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"
|
#define ALIAS_DB_MAP "netinfo:/aliases"
|
||||||
#include <libc.h>
|
#include <libc.h>
|
||||||
#define MISSING_POSIX_S_IS
|
#define MISSING_POSIX_S_IS
|
||||||
@ -1179,8 +1179,8 @@ typedef unsigned short mode_t;
|
|||||||
#define FIONREAD_IN_SYS_FILIO_H
|
#define FIONREAD_IN_SYS_FILIO_H
|
||||||
#define USE_SYS_SOCKIO_H
|
#define USE_SYS_SOCKIO_H
|
||||||
#define HAS_DBM
|
#define HAS_DBM
|
||||||
#define DEF_DB_TYPE "dbm"
|
#define NATIVE_DB_TYPE "dbm"
|
||||||
#define ALIAS_DB_MAP "dbm:/var/adm/sendmail/aliases"
|
#define ALIAS_DB_MAP DEF_DB_TYPE ":/var/adm/sendmail/aliases"
|
||||||
extern int optind; /* XXX use <getopt.h> */
|
extern int optind; /* XXX use <getopt.h> */
|
||||||
extern char *optarg; /* XXX use <getopt.h> */
|
extern char *optarg; /* XXX use <getopt.h> */
|
||||||
extern int opterr; /* 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 INTERNAL_LOCK MYFLOCK_STYLE_FCNTL
|
||||||
#define DEF_MAILBOX_LOCK "fcntl, dotlock"
|
#define DEF_MAILBOX_LOCK "fcntl, dotlock"
|
||||||
#define HAS_FSYNC
|
#define HAS_FSYNC
|
||||||
#define DEF_DB_TYPE "hash"
|
#define NATIVE_DB_TYPE "hash"
|
||||||
#define ALIAS_DB_MAP "hash:/etc/aliases"
|
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/aliases"
|
||||||
/* Uncomment the following line if you have NIS package installed */
|
/* Uncomment the following line if you have NIS package installed */
|
||||||
/* #define HAS_NIS */
|
/* #define HAS_NIS */
|
||||||
#define USE_SYS_SOCKIO_H
|
#define USE_SYS_SOCKIO_H
|
||||||
@ -1242,8 +1242,8 @@ extern int h_errno;
|
|||||||
#define DEF_MAILBOX_LOCK "fcntl, dotlock"
|
#define DEF_MAILBOX_LOCK "fcntl, dotlock"
|
||||||
#define HAS_FSYNC
|
#define HAS_FSYNC
|
||||||
#define HAS_DBM
|
#define HAS_DBM
|
||||||
#define DEF_DB_TYPE "dbm"
|
#define NATIVE_DB_TYPE "dbm"
|
||||||
#define ALIAS_DB_MAP "dbm:/etc/mail/aliases"
|
#define ALIAS_DB_MAP DEF_DB_TYPE ":/etc/mail/aliases"
|
||||||
#define DBM_NO_TRAILING_NULL
|
#define DBM_NO_TRAILING_NULL
|
||||||
#ifndef NO_NIS
|
#ifndef NO_NIS
|
||||||
#define HAS_NIS
|
#define HAS_NIS
|
||||||
@ -1303,6 +1303,10 @@ extern int h_errno;
|
|||||||
#endif
|
#endif
|
||||||
#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_CHAR_PTR_TO_INT(cptr) ((int) (long) (cptr))
|
||||||
#define CAST_INT_TO_CHAR_PTR(ival) ((char *) (long) (ival))
|
#define CAST_INT_TO_CHAR_PTR(ival) ((char *) (long) (ival))
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user