From: Wietse Venema Date: Mon, 21 Aug 2000 00:00:00 +0000 (+0000) Subject: snapshot-20000821 X-Git-Tag: v20010228~47 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=22a29015310cb5e33b6ed21866df307d749d00ef;p=thirdparty%2Fpostfix.git snapshot-20000821 --- diff --git a/postfix/HISTORY b/postfix/HISTORY index c7e387e87..17b46c128 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -4114,3 +4114,25 @@ Apologies for any names omitted. Robustness: make_dirs() now continues when a missing directory is created by another process. + +20000726 + + Robustness: added watchdog_pat() routine to keep the watchdog + quiet if a client stays connected for a lot of time. Files: + util/watchdog.[hc], smtpd/smtpd.c. + +20000729 + + Robustness: if relayhost is specified but the host does + not exist, defer mail instead of bouncing it (which would + lose the mail if the bounce would have to be delivered to + that same non-existent relayhost). Problem reported by + Chris Cooper @ maths.ox.ac.uk. + +20000821 + + Feature: added -r (replace) option to postalias and postmap. + + Cleanup: smtpd now replies with 555 when the client sends + unrecognized RCPT TO parameters, as required by RFC 1869 + (problem report by Robert Norris @ its.monash.edu.au). diff --git a/postfix/global/mail_version.h b/postfix/global/mail_version.h index 67b1245e8..9d6310f5b 100644 --- a/postfix/global/mail_version.h +++ b/postfix/global/mail_version.h @@ -15,7 +15,7 @@ * Version of this program. */ #define VAR_MAIL_VERSION "mail_version" -#define DEF_MAIL_VERSION "Snapshot-20000718" +#define DEF_MAIL_VERSION "Snapshot-20000821" extern char *var_mail_version; /* LICENSE diff --git a/postfix/global/opened.c b/postfix/global/opened.c index a01399cc9..169af05cd 100644 --- a/postfix/global/opened.c +++ b/postfix/global/opened.c @@ -6,10 +6,11 @@ /* SYNOPSIS /* #include /* -/* void opened(queue_id, sender, size, format, ...) +/* void opened(queue_id, sender, size, nrcpt, format, ...) /* const char *queue_id; /* const char *sender; /* long size; +/* int nrcpt; /* const char *format; /* DESCRIPTION /* opened() logs that a message was successfully delivered. @@ -23,6 +24,8 @@ /* Sender address. /* .IP size /* Message content size. +/* .IP nrcpt +/* Number of recipients. /* .IP format /* Format of optional text. /* DIAGNOSTICS @@ -58,25 +61,28 @@ /* opened - log that a message was opened */ -void opened(const char *queue_id, const char *sender, long size, const char *fmt,...) +void opened(const char *queue_id, const char *sender, long size, int nrcpt, + const char *fmt,...) { va_list ap; va_start(ap, fmt); - vopened(queue_id, sender, size, fmt, ap); + vopened(queue_id, sender, size, nrcpt, fmt, ap); va_end(ap); } -/* opened - log that a message was opened */ +/* vopened - log that a message was opened */ -void vopened(const char *queue_id, const char *sender, long size, const char *fmt, va_list ap) +void vopened(const char *queue_id, const char *sender, long size, int nrcpt, + const char *fmt, va_list ap) { VSTRING *text = vstring_alloc(100); #define TEXT (vstring_str(text)) vstring_vsprintf(text, fmt, ap); - msg_info("%s: from=<%s>, size=%ld%s%s%s", - queue_id, sender, size, *TEXT ? " (" : "", TEXT, *TEXT ? ")" : ""); + msg_info("%s: from=<%s>, size=%ld, nrcpt=%d%s%s%s", + queue_id, sender, size, nrcpt, + *TEXT ? " (" : "", TEXT, *TEXT ? ")" : ""); vstring_free(text); } diff --git a/postfix/global/opened.h b/postfix/global/opened.h index 618dd4221..3fc99598e 100644 --- a/postfix/global/opened.h +++ b/postfix/global/opened.h @@ -19,9 +19,10 @@ /* * External interface. */ -extern void PRINTFLIKE(4, 5) opened(const char *, const char *, long, +extern void PRINTFLIKE(5, 6) opened(const char *, const char *, long, int, const char *,...); -extern void vopened(const char *, const char *, long, const char *, va_list); +extern void vopened(const char *, const char *, long, int, + const char *, va_list); /* LICENSE /* .ad diff --git a/postfix/html/Makefile.in b/postfix/html/Makefile.in index 63f98693a..c701b0250 100644 --- a/postfix/html/Makefile.in +++ b/postfix/html/Makefile.in @@ -4,7 +4,8 @@ SHELL = /bin/sh DAEMONS = bounce.8.html cleanup.8.html defer.8.html error.8.html local.8.html \ lmtp.8.html master.8.html pickup.8.html pipe.8.html qmgr.8.html \ - showq.8.html smtp.8.html smtpd.8.html trivial-rewrite.8.html + showq.8.html smtp.8.html smtpd.8.html trivial-rewrite.8.html \ + nqmgr.8.html COMMANDS= mailq.1.html newaliases.1.html postalias.1.html postcat.1.html \ postconf.1.html postfix.1.html postkick.1.html postlock.1.html \ postlog.1.html postdrop.1.html postmap.1.html sendmail.1.html \ @@ -47,6 +48,10 @@ local.8.html: ../local/local.c master.8.html: ../master/master.c srctoman $? | nroff -man | man2html | postlink >$@ +nqmgr.8.html: ../nqmgr/qmgr.c + srctoman $? | sed -e 's/qmgr[^_]/n&/' -e 's/QMGR[^_]/N&/' | \ + nroff -man | man2html | postlink >$@ + pickup.8.html: ../pickup/pickup.c srctoman $? | nroff -man | man2html | postlink >$@ diff --git a/postfix/html/faq.html b/postfix/html/faq.html index f53e7d25b..2d4d47e0a 100644 --- a/postfix/html/faq.html +++ b/postfix/html/faq.html @@ -2080,7 +2080,7 @@ headers is sufficient to reliably implement a domain in a mailbox. /etc/postfix/virtual_regexp: /^virtual\.domain$/ whatever - /^(.*\)@virtual\.domain$/ joe+$1 + /^(.*)@virtual\.domain$/ joe+$1

diff --git a/postfix/html/nqmgr.8.html b/postfix/html/nqmgr.8.html new file mode 100644 index 000000000..4a96531e2 --- /dev/null +++ b/postfix/html/nqmgr.8.html @@ -0,0 +1,464 @@ +

+
+
+
+NQMGR(8)                                                 NQMGR(8)
+
+
+NAME
+       qmgr - Postfix queue manager
+
+SYNOPSIS
+       nqmgr [generic Postfix daemon options]
+
+DESCRIPTION
+       The  nqmgr  daemon awaits the arrival of incoming mail and
+       arranges for its delivery via Postfix delivery  processes.
+       The actual mail routing strategy is delegated to the triv-
+       ial-rewrite(8) daemon.  This program  expects  to  be  run
+       from the master(8) process manager.
+
+       Mail  addressed  to  the  local  double-bounce  address is
+       silently discarded.  This stops potential loops caused  by
+       undeliverable bounce notifications.
+
+       Mail  addressed to a user listed in the optional relocated
+       database is bounced with a "user has  moved  to  new_loca-
+       tion" message. See relocated(5) for a precise description.
+
+MAIL QUEUES
+       The nqmgr daemon maintains the following queues:
+
+       incoming
+              Inbound mail from the network, or mail picked up by
+              the local pickup agent from the maildrop directory.
+
+       active Messages that the  queue  manager  has  opened  for
+              delivery.  Only  a  limited  number  of messages is
+              allowed to enter the  active  queue  (leaky  bucket
+              strategy, for a fixed delivery rate).
+
+       deferred
+              Mail  that  could  not  be delivered upon the first
+              attempt. The queue manager  implements  exponential
+              backoff  by  doubling  the  time  between  delivery
+              attempts.
+
+       corrupt
+              Unreadable or damaged queue files  are  moved  here
+              for inspection.
+
+DELIVERY STATUS REPORTS
+       The nqmgr daemon keeps an eye on per-message delivery sta-
+       tus reports in  the  following  directories.  Each  status
+       report file has the same name as the corresponding message
+       file:
+
+       bounce Per-recipient status information about why mail  is
+              bounced.    These   files  are  maintained  by  the
+              bounce(8) daemon.
+
+       defer  Per-recipient status information about why mail  is
+
+
+
+                                                                1
+
+
+
+
+
+NQMGR(8)                                                 NQMGR(8)
+
+
+              delayed.    These   files  are  maintained  by  the
+              defer(8) daemon.
+
+       The nqmgr daemon is responsible for asking  the  bounce(8)
+       or defer(8) daemons to send non-delivery reports.
+
+STRATEGIES
+       The  queue  manager implements a variety of strategies for
+       either opening queue files (input) or for message delivery
+       (output).
+
+       leaky bucket
+              This  strategy limits the number of messages in the
+              active queue and prevents the  queue  manager  from
+              running out of memory under heavy load.
+
+       fairness
+              When  the  active queue has room, the queue manager
+              takes one message from the incoming queue  and  one
+              from the deferred queue. This prevents a large mail
+              backlog from blocking the delivery of new mail.
+
+       slow start
+              This strategy eliminates "thundering herd" problems
+              by slowly adjusting the number of parallel deliver-
+              ies to the same destination.
+
+       round robin
+              The queue manager sorts delivery requests by desti-
+              nation.   Round-robin selection prevents one desti-
+              nation from dominating deliveries to other destina-
+              tions.
+
+       exponential backoff
+              Mail  that  cannot  be  delivered  upon  the  first
+              attempt is deferred.   The  time  interval  between
+              delivery attempts is doubled after each attempt.
+
+       destination status cache
+              The   queue  manager  avoids  unnecessary  delivery
+              attempts by  maintaining  a  short-term,  in-memory
+              list of unreachable destinations.
+
+       preemptive message scheduling
+              The  queue manager attempts to minimize the average
+              per-recipient delay while still preserving the cor-
+              rect per-message delays, using a sophisticated pre-
+              emptive message scheduling.
+
+TRIGGERS
+       On an idle system, the queue manager waits for the arrival
+       of  trigger  events,  or it waits for a timer to go off. A
+       trigger is a one-byte message.  Depending on  the  message
+       received,  the queue manager performs one of the following
+
+
+
+                                                                2
+
+
+
+
+
+NQMGR(8)                                                 NQMGR(8)
+
+
+       actions (the message is followed by the symbolic  constant
+       used internally by the software):
+
+       D (QMGR_REQ_SCAN_DEFERRED)
+              Start  a  deferred queue scan.  If a deferred queue
+              scan is already in  progress,  that  scan  will  be
+              restarted as soon as it finishes.
+
+       I (QMGR_REQ_SCAN_INCOMING)
+              Start  an incoming queue scan. If an incoming queue
+              scan is already in  progress,  that  scan  will  be
+              restarted as soon as it finishes.
+
+       A (QMGR_REQ_SCAN_ALL)
+              Ignore deferred queue file time stamps. The request
+              affects the next deferred queue scan.
+
+       F (QMGR_REQ_FLUSH_DEAD)
+              Purge all information  about  dead  transports  and
+              destinations.
+
+       W (TRIGGER_REQ_WAKEUP)
+              Wakeup  call,  This is used by the master server to
+              instantiate servers that should not  go  away  for-
+              ever.  The  action  is  to  start an incoming queue
+              scan.
+
+       The nqmgr daemon reads an entire buffer worth of triggers.
+       Multiple  identical  trigger  requests  are collapsed into
+       one, and trigger requests are sorted so that A and F  pre-
+       cede  D  and  I.  Thus, in order to force a deferred queue
+       run, one would request A F D; in order to notify the queue
+       manager of the arrival of new mail one would request I.
+
+STANDARDS
+       None.  The nqmgr daemon does not interact with the outside
+       world.
+
+SECURITY
+       The nqmgr daemon is not security sensitive. It reads  sin-
+       gle-character  messages  from  untrusted  local users, and
+       thus may be susceptible to denial of service attacks.  The
+       nqmgr  daemon  does  not talk to the outside world, and it
+       can be run at fixed low privilege in a  chrooted  environ-
+       ment.
+
+DIAGNOSTICS
+       Problems and transactions are logged to the syslog daemon.
+       Corrupted message files are saved to the corrupt queue for
+       further inspection.
+
+       Depending  on the setting of the notify_classes parameter,
+       the postmaster is notified of bounces and of  other  trou-
+       ble.
+
+
+
+                                                                3
+
+
+
+
+
+NQMGR(8)                                                 NQMGR(8)
+
+
+BUGS
+       A  single  queue  manager  process has to compete for disk
+       access with multiple front-end processes such as smtpd.  A
+       sudden  burst  of  inbound mail can negatively impact out-
+       bound delivery rates.
+
+CONFIGURATION PARAMETERS
+       The following main.cf parameters are  especially  relevant
+       to  this  program. See the Postfix main.cf file for syntax
+       details and for default values.  Use  the  postfix  reload
+       command after a configuration change.
+
+Miscellaneous
+       allow_min_user
+              Do  not  bounce recipient addresses that begin with
+              '-'.
+
+       relocated_maps
+              Tables with contact information for users, hosts or
+              domains that no longer exist. See relocated(5).
+
+       queue_directory
+              Top-level directory of the Postfix queue.
+
+Active queue controls
+       In  the text below, transport is the first field in a mas-
+       ter.cf entry.
+
+       qmgr_message_active_limit
+              Limit the number of messages in the active queue.
+
+       qmgr_message_recipient_limit
+              Limit the number of in-memory recipients.
+
+              This parameter also limits the size of  the  short-
+              term, in-memory destination cache.
+
+       qmgr_message_recipient_minimum
+              Per message minimum of in-memory recipients.
+
+       default_recipient_limit
+              Default limit on the number of in-memory recipients
+              per transport.
+
+       transport_recipient_limit
+              Limit on the number of  in-memory  recipients,  for
+              the named message transport.
+
+       default_extra_recipient_limit
+              Default  limit on the total number of per transport
+              in-memory recipients that the  preempting  messages
+              can have.
+
+
+
+
+
+                                                                4
+
+
+
+
+
+NQMGR(8)                                                 NQMGR(8)
+
+
+       transport_extra_recipient_limit
+              Limit  on  the number of in-memory recipients which
+              all preempting messages delivered by the  transport
+              transport can have.
+
+Timing controls
+       min_backoff
+              Minimal  time  in seconds between delivery attempts
+              of a deferred message.
+
+              This parameter also limits the time an  unreachable
+              destination  is  kept  in the short-term, in-memory
+              destination status cache.
+
+       max_backoff
+              Maximal time in seconds between  delivery  attempts
+              of a deferred message.
+
+       maximal_queue_lifetime
+              Maximal  time in days a message is queued before it
+              is sent back as undeliverable.
+
+       queue_run_delay
+              Time in seconds between deferred queue scans. Queue
+              scans do not overlap.
+
+       transport_retry_time
+              Time  in seconds between attempts to contact a bro-
+              ken delivery transport.
+
+Concurrency controls
+       initial_destination_concurrency
+              Initial per-destination concurrency level for  par-
+              allel delivery to the same destination.
+
+       default_destination_concurrency_limit
+              Default  limit on the number of parallel deliveries
+              to the same destination.
+
+       transport_destination_concurrency_limit
+              Limit on the number of parallel deliveries  to  the
+              same  destination,  for delivery via the named mes-
+              sage transport.
+
+Recipient controls
+       default_destination_recipient_limit
+              Default limit on the number of recipients per  mes-
+              sage transfer.
+
+       transport_destination_recipient_limit
+              Limit  on  the  number  of  recipients  per message
+              transfer, for the named message transport.
+
+
+
+
+
+                                                                5
+
+
+
+
+
+NQMGR(8)                                                 NQMGR(8)
+
+
+Message scheduling
+       transport_delivery_slot_cost (valid range: 0,2,3...)
+              This parameter basically controls how often a  mes-
+              sage  delivered  by  transport  can be preempted by
+              another message.  An internal per-message/transport
+              counter  is  incremented  by  one  for  each trans-
+              port_delivery_slot_cost   deliveries   handled   by
+              transport.  This  counter  represents the number of
+              "available delivery slots" for use  by  other  mes-
+              sages.  Current message can be preempted by another
+              message when that other message  can  be  delivered
+              using  less  tranpsort agents than the value of the
+              "available delivery slots" counter.
+
+              Value equal to 0 disables  the  message  preemption
+              for transport.
+
+       transport_minimum_delivery_slots
+              Message preemption is not attempted at all whenever
+              a message  that  can't  ever  accumulate  at  least
+              transport_minimum_delivery_slots available delivery
+              slots is being delivered by transport.
+
+       transport_delivery_slot_discount (valid range: 0..100)
+
+       transport_delivery_slot_loan
+              These parameters speed up the moment when a message
+              preemption  can  happen.   Instead of waiting until
+              the full  amount  of  delivery  slots  required  is
+              available,  the  preemption  can happen when trans-
+              port_delivery_slot_discount percent of the required
+              amount   plus   transport_delivery_slot_loan  still
+              remains to  be  accumulated.  Note  that  the  full
+              amount  will  still  have  to be accumulated before
+              another preemption can take place later.
+
+       default_delivery_slot_cost
+
+       default_minimum_delivery_slots
+
+       default_delivery_slot_discount
+
+       default_delivery_slot_loan
+              Default values for the transport  specific  parame-
+              ters described above.
+
+SEE ALSO
+       master(8), process manager
+       relocated(5), format of the "user has moved" table
+       syslogd(8) system logging
+       trivial-rewrite(8), address routing
+
+LICENSE
+       The  Secure  Mailer  license must be distributed with this
+
+
+
+                                                                6
+
+
+
+
+
+NQMGR(8)                                                 NQMGR(8)
+
+
+       software.
+
+AUTHOR(S)
+       Wietse Venema
+       IBM T.J. Watson Research
+       P.O. Box 704
+       Yorktown Heights, NY 10598, USA
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+                                                                7
+
+
+
diff --git a/postfix/html/postalias.1.html b/postfix/html/postalias.1.html index c66bc04ae..e8540371f 100644 --- a/postfix/html/postalias.1.html +++ b/postfix/html/postalias.1.html @@ -9,7 +9,7 @@ POSTALIAS(1) POSTALIAS(1) postalias - Postfix alias database maintenance SYNOPSIS - postalias [-Ninvw] [-c config_dir] [-d key] [-q key] + postalias [-Ninrvw] [-c config_dir] [-d key] [-q key] [file_type:]file_name ... DESCRIPTION @@ -55,10 +55,10 @@ POSTALIAS(1) POSTALIAS(1) The exit status is non-zero if the requested infor- mation was not found. - -v Enable verbose logging for debugging purposes. Mul- - tiple -v options make the software increasingly - verbose. + -r When updating a table, do not warn about duplicate + entries; silently replace them. + -v Enable verbose logging for debugging purposes. @@ -71,7 +71,10 @@ POSTALIAS(1) POSTALIAS(1) POSTALIAS(1) POSTALIAS(1) - -w When updating a table, do not warn about duplicate + Multiple -v options make the software increasingly + verbose. + + -w When updating a table, do not warn about duplicate entries; silently ignore them. Arguments: @@ -79,35 +82,35 @@ POSTALIAS(1) POSTALIAS(1) file_type The type of database to be produced. - btree The output is a btree file, named - file_name.db. This is available only on + btree The output is a btree file, named + file_name.db. This is available only on systems with support for db databases. - dbm The output consists of two files, named - file_name.pag and file_name.dir. This is - available only on systems with support for + dbm The output consists of two files, named + file_name.pag and file_name.dir. This is + available only on systems with support for dbm databases. - hash The output is a hashed file, named - file_name.db. This is available only on + hash The output is a hashed file, named + file_name.db. This is available only on systems with support for db databases. - When no file_type is specified, the software uses - the database type specified via the database_type - configuration parameter. The default value for + When no file_type is specified, the software uses + the database type specified via the database_type + configuration parameter. The default value for this parameter depends on the host environment. file_name - The name of the alias database source file when + The name of the alias database source file when rebuilding a database. DIAGNOSTICS - Problems are logged to the standard error stream. No out- + Problems are logged to the standard error stream. No out- put means no problems were detected. Duplicate entries are skipped and are flagged with a warning. BUGS - The "delete key" support is limited to one delete opera- + The "delete key" support is limited to one delete opera- tion per command invocation. ENVIRONMENT @@ -118,13 +121,10 @@ POSTALIAS(1) POSTALIAS(1) Enable verbose logging for debugging purposes. CONFIGURATION PARAMETERS - The following main.cf parameters are especially relevant - to this program. See the Postfix main.cf file for syntax + The following main.cf parameters are especially relevant + to this program. See the Postfix main.cf file for syntax details and for default values. - database_type - Default alias database type. On many UNIX systems, - the default type is either dbm or hash. @@ -137,15 +137,19 @@ POSTALIAS(1) POSTALIAS(1) POSTALIAS(1) POSTALIAS(1) + database_type + Default alias database type. On many UNIX systems, + the default type is either dbm or hash. + STANDARDS - RFC 822 (ARPA Internet Text Messages) + RFC 822 (ARPA Internet Text Messages) SEE ALSO aliases(5) format of alias database input file. sendmail(1) mail posting and compatibility interface. LICENSE - The Secure Mailer license must be distributed with this + The Secure Mailer license must be distributed with this software. AUTHOR(S) @@ -185,10 +189,6 @@ POSTALIAS(1) POSTALIAS(1) - - - - diff --git a/postfix/html/postmap.1.html b/postfix/html/postmap.1.html index 9dee8109d..b81278cbc 100644 --- a/postfix/html/postmap.1.html +++ b/postfix/html/postmap.1.html @@ -9,7 +9,7 @@ POSTMAP(1) POSTMAP(1) postmap - Postfix lookup table management SYNOPSIS - postmap [-Ninvw] [-c config_dir] [-d key] [-q key] + postmap [-Ninrvw] [-c config_dir] [-d key] [-q key] [file_type:]file_name ... DESCRIPTION @@ -86,11 +86,14 @@ POSTMAP(1) POSTMAP(1) The exit status is non-zero if the requested infor- mation was not found. + -r When updating a table, do not warn about duplicate + entries; silently replace them. + -v Enable verbose logging for debugging purposes. Mul- - tiple -v options make the software increasingly + tiple -v options make the software increasingly verbose. - -w When updating a table, do not warn about duplicate + -w When updating a table, do not warn about duplicate entries; silently ignore them. Arguments: @@ -98,33 +101,30 @@ POSTMAP(1) POSTMAP(1) file_type The type of database to be produced. - btree The output file is a btree file, named - file_name.db. This is available only on + btree The output file is a btree file, named + file_name.db. This is available only on systems with support for db databases. - dbm The output consists of two files, named - file_name.pag and file_name.dir. This is - available only on systems with support for + dbm The output consists of two files, named + file_name.pag and file_name.dir. This is + available only on systems with support for dbm databases. - hash The output file is a hashed file, named - file_name.db. This is available only on + hash The output file is a hashed file, named + file_name.db. This is available only on systems with support for db databases. - When no file_type is specified, the software uses - the database type specified via the database_type + When no file_type is specified, the software uses + the database type specified via the database_type configuration parameter. file_name - The name of the lookup table source file when + The name of the lookup table source file when rebuilding a database. DIAGNOSTICS Problems and transactions are logged to the standard error stream. No output means no problems. Duplicate entries are - skipped and are flagged with a warning. - - @@ -137,8 +137,10 @@ POSTMAP(1) POSTMAP(1) POSTMAP(1) POSTMAP(1) + skipped and are flagged with a warning. + BUGS - The "delete key" support is limited to one delete opera- + The "delete key" support is limited to one delete opera- tion per command invocation. ENVIRONMENT @@ -150,12 +152,12 @@ POSTMAP(1) POSTMAP(1) CONFIGURATION PARAMETERS database_type - Default output database type. On many UNIX sys- - tems, the default database type is either hash or + Default output database type. On many UNIX sys- + tems, the default database type is either hash or dbm. LICENSE - The Secure Mailer license must be distributed with this + The Secure Mailer license must be distributed with this software. AUTHOR(S) @@ -189,8 +191,6 @@ POSTMAP(1) POSTMAP(1) - - diff --git a/postfix/html/transport.5.html b/postfix/html/transport.5.html index 17a2385a4..9b760ccd3 100644 --- a/postfix/html/transport.5.html +++ b/postfix/html/transport.5.html @@ -122,7 +122,7 @@ TRANSPORT(5) TRANSPORT(5) .foo.org error:mail for *.foo.org is not deliverable - This causes all mail for user@anythingfoo.org + This causes all mail for user@anything.foo.org to be bounced. diff --git a/postfix/man/Makefile.in b/postfix/man/Makefile.in index 235143125..44368bbd1 100644 --- a/postfix/man/Makefile.in +++ b/postfix/man/Makefile.in @@ -4,7 +4,8 @@ SHELL = /bin/sh DAEMONS = man8/bounce.8 man8/defer.8 man8/cleanup.8 man8/error.8 man8/local.8 \ man8/lmtp.8 man8/master.8 man8/pickup.8 man8/pipe.8 man8/qmgr.8 \ - man8/showq.8 man8/smtp.8 man8/smtpd.8 man8/trivial-rewrite.8 + man8/showq.8 man8/smtp.8 man8/smtpd.8 man8/trivial-rewrite.8 \ + man8/nqmgr.8 COMMANDS= man1/postalias.1 man1/postcat.1 man1/postconf.1 man1/postfix.1 \ man1/postkick.1 man1/postlock.1 man1/postlog.1 man1/postdrop.1 \ man1/postmap.1 man1/sendmail.1 man1/mailq.1 man1/newaliases.1 \ @@ -46,6 +47,10 @@ man8/lmtp.8: ../lmtp/lmtp.c man8/master.8: ../master/master.c ../mantools/srctoman $? >$@ +man8/nqmgr.8: ../nqmgr/qmgr.c + ../mantools/srctoman $? | \ + sed -e 's/qmgr[^_]/n&/' -e 's/QMGR[^_]/N&/' >$@ + man8/pickup.8: ../pickup/pickup.c ../mantools/srctoman $? >$@ diff --git a/postfix/man/man1/postalias.1 b/postfix/man/man1/postalias.1 index a2b3e652e..88bc2242c 100644 --- a/postfix/man/man1/postalias.1 +++ b/postfix/man/man1/postalias.1 @@ -9,7 +9,7 @@ Postfix alias database maintenance .na .nf .fi -\fBpostalias\fR [\fB-Ninvw\fR] [\fB-c \fIconfig_dir\fR] +\fBpostalias\fR [\fB-Ninrvw\fR] [\fB-c \fIconfig_dir\fR] [\fB-d \fIkey\fR] [\fB-q \fIkey\fR] [\fIfile_type\fR:]\fIfile_name\fR ... .SH DESCRIPTION @@ -48,6 +48,9 @@ the host operating system. Search the specified maps for \fIkey\fR and print the first value found on the standard output stream. The exit status is non-zero if the requested information was not found. +.IP \fB-r\fR +When updating a table, do not warn about duplicate entries; silently +replace them. .IP \fB-v\fR Enable verbose logging for debugging purposes. Multiple \fB-v\fR options make the software increasingly verbose. diff --git a/postfix/man/man1/postmap.1 b/postfix/man/man1/postmap.1 index 1db5db8ac..384d624fb 100644 --- a/postfix/man/man1/postmap.1 +++ b/postfix/man/man1/postmap.1 @@ -9,7 +9,7 @@ Postfix lookup table management .na .nf .fi -\fBpostmap\fR [\fB-Ninvw\fR] [\fB-c \fIconfig_dir\fR] [\fB-d \fIkey\fR] +\fBpostmap\fR [\fB-Ninrvw\fR] [\fB-c \fIconfig_dir\fR] [\fB-d \fIkey\fR] [\fB-q \fIkey\fR] [\fIfile_type\fR:]\fIfile_name\fR ... .SH DESCRIPTION .ad @@ -66,6 +66,9 @@ the host operating system. Search the specified maps for \fIkey\fR and print the first value found on the standard output stream. The exit status is non-zero if the requested information was not found. +.IP \fB-r\fR +When updating a table, do not warn about duplicate entries; silently +replace them. .IP \fB-v\fR Enable verbose logging for debugging purposes. Multiple \fB-v\fR options make the software increasingly verbose. diff --git a/postfix/man/man5/transport.5 b/postfix/man/man5/transport.5 index 762bd8097..12fa3c273 100644 --- a/postfix/man/man5/transport.5 +++ b/postfix/man/man5/transport.5 @@ -115,7 +115,7 @@ The error mailer can be used to bounce mail: .ti +5 \fB\&.foo.org error:mail for *.foo.org is not deliverable\fR -This causes all mail for \fIuser\fR@\fIanything\fBfoo.org\fR +This causes all mail for \fIuser\fR@\fIanything\fB.foo.org\fR to be bounced. .SH REGULAR EXPRESSION TABLES .na diff --git a/postfix/man/man8/nqmgr.8 b/postfix/man/man8/nqmgr.8 new file mode 100644 index 000000000..e66ad522a --- /dev/null +++ b/postfix/man/man8/nqmgr.8 @@ -0,0 +1,302 @@ +.TH NQMGR 8 +.ad +.fi +.SH NAME +qmgr +\- +Postfix queue manager +.SH SYNOPSIS +.na +.nf +\fBnqmgr\fR [generic Postfix daemon options] +.SH DESCRIPTION +.ad +.fi +The \fBnqmgr\fR daemon awaits the arrival of incoming mail +and arranges for its delivery via Postfix delivery processes. +The actual mail routing strategy is delegated to the +\fBtrivial-rewrite\fR(8) daemon. +This program expects to be run from the \fBmaster\fR(8) process +manager. + +Mail addressed to the local \fBdouble-bounce\fR address is silently +discarded. This stops potential loops caused by undeliverable +bounce notifications. + +Mail addressed to a user listed in the optional \fBrelocated\fR +database is bounced with a "user has moved to \fInew_location\fR" +message. See \fBrelocated\fR(5) for a precise description. +.SH MAIL QUEUES +.na +.nf +.ad +.fi +The \fBnqmgr\fR daemon maintains the following queues: +.IP \fBincoming\fR +Inbound mail from the network, or mail picked up by the +local \fBpickup\fR agent from the \fBmaildrop\fR directory. +.IP \fBactive\fR +Messages that the queue manager has opened for delivery. Only +a limited number of messages is allowed to enter the \fBactive\fR +queue (leaky bucket strategy, for a fixed delivery rate). +.IP \fBdeferred\fR +Mail that could not be delivered upon the first attempt. The queue +manager implements exponential backoff by doubling the time between +delivery attempts. +.IP \fBcorrupt\fR +Unreadable or damaged queue files are moved here for inspection. +.SH DELIVERY STATUS REPORTS +.na +.nf +.ad +.fi +The \fBnqmgr\fR daemon keeps an eye on per-message delivery status +reports in the following directories. Each status report file has +the same name as the corresponding message file: +.IP \fBbounce\fR +Per-recipient status information about why mail is bounced. +These files are maintained by the \fBbounce\fR(8) daemon. +.IP \fBdefer\fR +Per-recipient status information about why mail is delayed. +These files are maintained by the \fBdefer\fR(8) daemon. +.PP +The \fBnqmgr\fR daemon is responsible for asking the +\fBbounce\fR(8) or \fBdefer\fR(8) daemons to send non-delivery +reports. +.SH STRATEGIES +.na +.nf +.ad +.fi +The queue manager implements a variety of strategies for +either opening queue files (input) or for message delivery (output). +.IP "\fBleaky bucket\fR" +This strategy limits the number of messages in the \fBactive\fR queue +and prevents the queue manager from running out of memory under +heavy load. +.IP \fBfairness\fR +When the \fBactive\fR queue has room, the queue manager takes one +message from the \fBincoming\fR queue and one from the \fBdeferred\fR +queue. This prevents a large mail backlog from blocking the delivery +of new mail. +.IP "\fBslow start\fR" +This strategy eliminates "thundering herd" problems by slowly +adjusting the number of parallel deliveries to the same destination. +.IP "\fBround robin\fR +The queue manager sorts delivery requests by destination. +Round-robin selection prevents one destination from dominating +deliveries to other destinations. +.IP "\fBexponential backoff\fR" +Mail that cannot be delivered upon the first attempt is deferred. +The time interval between delivery attempts is doubled after each +attempt. +.IP "\fBdestination status cache\fR" +The queue manager avoids unnecessary delivery attempts by +maintaining a short-term, in-memory list of unreachable destinations. +.IP "\fBpreemptive message scheduling\fR" +The queue manager attempts to minimize the average per-recipient delay +while still preserving the correct per-message delays, using +a sophisticated preemptive message scheduling. +.SH TRIGGERS +.na +.nf +.ad +.fi +On an idle system, the queue manager waits for the arrival of +trigger events, or it waits for a timer to go off. A trigger +is a one-byte message. +Depending on the message received, the queue manager performs +one of the following actions (the message is followed by the +symbolic constant used internally by the software): +.IP "\fBD (QMGR_REQ_SCAN_DEFERRED)\fR" +Start a deferred queue scan. If a deferred queue scan is already +in progress, that scan will be restarted as soon as it finishes. +.IP "\fBI (QMGR_REQ_SCAN_INCOMING)\fR" +Start an incoming queue scan. If an incoming queue scan is already +in progress, that scan will be restarted as soon as it finishes. +.IP "\fBA (QMGR_REQ_SCAN_ALL)\fR" +Ignore deferred queue file time stamps. The request affects +the next deferred queue scan. +.IP "\fBF (QMGR_REQ_FLUSH_DEAD)\fR" +Purge all information about dead transports and destinations. +.IP "\fBW (TRIGGER_REQ_WAKEUP)\fR" +Wakeup call, This is used by the master server to instantiate +servers that should not go away forever. The action is to start +an incoming queue scan. +.PP +The \fBnqmgr\fR daemon reads an entire buffer worth of triggers. +Multiple identical trigger requests are collapsed into one, and +trigger requests are sorted so that \fBA\fR and \fBF\fR precede +\fBD\fR and \fBI\fR. Thus, in order to force a deferred queue run, +one would request \fBA F D\fR; in order to notify the queue manager +of the arrival of new mail one would request \fBI\fR. +.SH STANDARDS +.na +.nf +.ad +.fi +None. The \fBnqmgr\fR daemon does not interact with the outside world. +.SH SECURITY +.na +.nf +.ad +.fi +The \fBnqmgr\fR daemon is not security sensitive. It reads +single-character messages from untrusted local users, and thus may +be susceptible to denial of service attacks. The \fBnqmgr\fR daemon +does not talk to the outside world, and it can be run at fixed low +privilege in a chrooted environment. +.SH DIAGNOSTICS +.ad +.fi +Problems and transactions are logged to the syslog daemon. +Corrupted message files are saved to the \fBcorrupt\fR queue +for further inspection. + +Depending on the setting of the \fBnotify_classes\fR parameter, +the postmaster is notified of bounces and of other trouble. +.SH BUGS +.ad +.fi +A single queue manager process has to compete for disk access with +multiple front-end processes such as \fBsmtpd\fR. A sudden burst of +inbound mail can negatively impact outbound delivery rates. +.SH CONFIGURATION PARAMETERS +.na +.nf +.ad +.fi +The following \fBmain.cf\fR parameters are especially relevant to +this program. See the Postfix \fBmain.cf\fR file for syntax details +and for default values. Use the \fBpostfix reload\fR command after +a configuration change. +.SH Miscellaneous +.ad +.fi +.IP \fBallow_min_user\fR +Do not bounce recipient addresses that begin with '-'. +.IP \fBrelocated_maps\fR +Tables with contact information for users, hosts or domains +that no longer exist. See \fBrelocated\fR(5). +.IP \fBqueue_directory\fR +Top-level directory of the Postfix queue. +.SH "Active queue controls" +.ad +.fi +In the text below, \fItransport\fR is the first field in a +\fBmaster.cf\fR entry. +.IP \fBqmgr_message_active_limit\fR +Limit the number of messages in the active queue. +.IP \fBqmgr_message_recipient_limit\fR +Limit the number of in-memory recipients. +.sp +This parameter also limits the size of the short-term, in-memory +destination cache. +.IP \fBqmgr_message_recipient_minimum\fR +Per message minimum of in-memory recipients. +.IP \fBdefault_recipient_limit\fR +Default limit on the number of in-memory recipients per transport. +.IP \fItransport\fB_recipient_limit\fR +Limit on the number of in-memory recipients, for the named +message \fItransport\fR. +.IP \fBdefault_extra_recipient_limit\fR +Default limit on the total number of per transport in-memory +recipients that the preempting messages can have. +.IP \fItransport\fB_extra_recipient_limit\fR +Limit on the number of in-memory recipients which all preempting +messages delivered by the transport \fItransport\fR can have. +.SH "Timing controls" +.ad +.fi +.IP \fBmin_backoff\fR +Minimal time in seconds between delivery attempts +of a deferred message. +.sp +This parameter also limits the time an unreachable destination +is kept in the short-term, in-memory destination status cache. +.IP \fBmax_backoff\fR +Maximal time in seconds between delivery attempts +of a deferred message. +.IP \fBmaximal_queue_lifetime\fR +Maximal time in days a message is queued +before it is sent back as undeliverable. +.IP \fBqueue_run_delay\fR +Time in seconds between deferred queue scans. Queue scans do +not overlap. +.IP \fBtransport_retry_time\fR +Time in seconds between attempts to contact a broken +delivery transport. +.SH "Concurrency controls" +.ad +.fi +.IP \fBinitial_destination_concurrency\fR +Initial per-destination concurrency level for parallel delivery +to the same destination. +.IP \fBdefault_destination_concurrency_limit\fR +Default limit on the number of parallel deliveries to the same +destination. +.IP \fItransport\fB_destination_concurrency_limit\fR +Limit on the number of parallel deliveries to the same destination, +for delivery via the named message \fItransport\fR. +.SH "Recipient controls" +.ad +.fi +.IP \fBdefault_destination_recipient_limit\fR +Default limit on the number of recipients per message transfer. +.IP \fItransport\fB_destination_recipient_limit\fR +Limit on the number of recipients per message transfer, for the +named message \fItransport\fR. +.SH "Message scheduling" +.ad +.fi +.IP "\fItransport\fB_delivery_slot_cost\fR (valid range: 0,2,3...) +This parameter basically controls how often a message +delivered by \fItransport\fB can be preempted by another +message. +An internal per-message/transport counter is incremented by one +for each \fItransport\fB_delivery_slot_cost\fR +deliveries handled by \fItransport\fR. This counter represents +the number of "available delivery slots" for use by other messages. +Current message can be preempted by another message when that +other message can be delivered using less \fItranpsort\fR agents +than the value of the "available delivery slots" counter. +.sp +Value equal to 0 disables the message preemption for \fItransport\fR. +.IP \fItransport\fB_minimum_delivery_slots\fR +Message preemption is not attempted at all whenever a message +that can't ever accumulate at least \fItransport\fB_minimum_delivery_slots\fR +available delivery slots is being delivered by \fItransport\fR. +.IP "\fItransport\fB_delivery_slot_discount\fR (valid range: 0..100)" +.IP \fItransport\fB_delivery_slot_loan\fR +These parameters speed up the moment when a message preemption can happen. +Instead of waiting until the full amount of delivery slots +required is available, the preemption can happen when +\fItransport\fB_delivery_slot_discount\fR percent of the required +amount plus \fItransport\fB_delivery_slot_loan\fR still remains to +be accumulated. Note that the full amount will still have to be +accumulated before another preemption can take place later. +.IP \fBdefault_delivery_slot_cost\fR +.IP \fBdefault_minimum_delivery_slots\fR +.IP \fBdefault_delivery_slot_discount\fR +.IP \fBdefault_delivery_slot_loan\fR +Default values for the transport specific parameters described above. +.SH SEE ALSO +.na +.nf +master(8), process manager +relocated(5), format of the "user has moved" table +syslogd(8) system logging +trivial-rewrite(8), address routing +.SH LICENSE +.na +.nf +.ad +.fi +The Secure Mailer license must be distributed with this software. +.SH AUTHOR(S) +.na +.nf +Wietse Venema +IBM T.J. Watson Research +P.O. Box 704 +Yorktown Heights, NY 10598, USA diff --git a/postfix/nqmgr/qmgr.c b/postfix/nqmgr/qmgr.c index aff5a8bd9..74acca9e1 100644 --- a/postfix/nqmgr/qmgr.c +++ b/postfix/nqmgr/qmgr.c @@ -83,8 +83,8 @@ /* maintaining a short-term, in-memory list of unreachable destinations. /* .IP "\fBpreemptive message scheduling\fR" /* The queue manager attempts to minimize the average per-recipient delay -/* while still assuring equality of average per-message delays, using -/* sophisticated preemptive message scheduling. +/* while still preserving the correct per-message delays, using +/* a sophisticated preemptive message scheduling. /* TRIGGERS /* .ad /* .fi @@ -180,7 +180,7 @@ /* recipients that the preempting messages can have. /* .IP \fItransport\fB_extra_recipient_limit\fR /* Limit on the number of in-memory recipients which all preempting -/* messages delivered by transport \fItransport\fR can have. +/* messages delivered by the transport \fItransport\fR can have. /* .SH "Timing controls" /* .ad /* .fi @@ -245,7 +245,7 @@ /* .IP "\fItransport\fB_delivery_slot_discount\fR (valid range: 0..100)" /* .IP \fItransport\fB_delivery_slot_loan\fR /* These parameters speed up the moment when a message preemption can happen. -/* Instead of waiting till the full amount of delivery slots +/* Instead of waiting until the full amount of delivery slots /* required is available, the preemption can happen when /* \fItransport\fB_delivery_slot_discount\fR percent of the required /* amount plus \fItransport\fB_delivery_slot_loan\fR still remains to @@ -327,9 +327,6 @@ char *var_relocated_maps; char *var_virtual_maps; char *var_defer_xports; bool var_allow_min_user; -int var_qmgr_hog; /* XXX */ -int var_qmgr_fudge; /* XXX */ -int var_local_rcpt_lim; /* XXX */ static QMGR_SCAN *qmgr_incoming; static QMGR_SCAN *qmgr_deferred; @@ -435,9 +432,8 @@ static int qmgr_loop(char *unused_name, char **unused_argv) /* * Let some new blood into the active queue when the queue size is - * smaller than some configurable limit, and when the number of in-core - * recipients does not exceed some configurable limit. When the system is - * under heavy load, favor new mail over old mail. + * smaller than some configurable limit. When the system is under heavy + * load, favor new mail over old mail. */ if (qmgr_message_count < var_qmgr_active_limit) if ((in_path = qmgr_scan_next(qmgr_incoming)) != 0) @@ -513,7 +509,7 @@ int main(int argc, char **argv) VAR_QUEUE_RUN_DELAY, DEF_QUEUE_RUN_DELAY, &var_queue_run_delay, 1, 0, VAR_MIN_BACKOFF_TIME, DEF_MIN_BACKOFF_TIME, &var_min_backoff_time, 1, 0, VAR_MAX_BACKOFF_TIME, DEF_MAX_BACKOFF_TIME, &var_max_backoff_time, 1, 0, - VAR_MAX_QUEUE_TIME, DEF_MAX_QUEUE_TIME, &var_max_queue_time, 1, 0, + VAR_MAX_QUEUE_TIME, DEF_MAX_QUEUE_TIME, &var_max_queue_time, 1, 1000, VAR_QMGR_ACT_LIMIT, DEF_QMGR_ACT_LIMIT, &var_qmgr_active_limit, 1, 0, VAR_QMGR_RCPT_LIMIT, DEF_QMGR_RCPT_LIMIT, &var_qmgr_rcpt_limit, 0, 0, VAR_QMGR_MSG_RCPT_LIMIT, DEF_QMGR_MSG_RCPT_LIMIT, &var_qmgr_msg_rcpt_limit, 1, 0, @@ -527,9 +523,6 @@ int main(int argc, char **argv) VAR_XPORT_RETRY_TIME, DEF_XPORT_RETRY_TIME, &var_transport_retry_time, 1, 0, VAR_DEST_CON_LIMIT, DEF_DEST_CON_LIMIT, &var_dest_con_limit, 0, 0, VAR_DEST_RCPT_LIMIT, DEF_DEST_RCPT_LIMIT, &var_dest_rcpt_limit, 0, 0, - VAR_QMGR_HOG, DEF_QMGR_HOG, &var_qmgr_hog, 10, 100, - VAR_QMGR_FUDGE, DEF_QMGR_FUDGE, &var_qmgr_fudge, 10, 100, - VAR_LOCAL_RCPT_LIMIT, DEF_LOCAL_RCPT_LIMIT, &var_local_rcpt_lim, 0, 0, 0, }; static CONFIG_BOOL_TABLE bool_table[] = { diff --git a/postfix/nqmgr/qmgr.h b/postfix/nqmgr/qmgr.h index a47db71fd..5982b4f60 100644 --- a/postfix/nqmgr/qmgr.h +++ b/postfix/nqmgr/qmgr.h @@ -28,7 +28,7 @@ typedef struct QMGR_QUEUE QMGR_QUEUE; typedef struct QMGR_ENTRY QMGR_ENTRY; typedef struct QMGR_MESSAGE QMGR_MESSAGE; typedef struct QMGR_JOB QMGR_JOB; -typedef struct QMGR_PEER QMGR_PEER ; +typedef struct QMGR_PEER QMGR_PEER; typedef struct QMGR_TRANSPORT_LIST QMGR_TRANSPORT_LIST; typedef struct QMGR_QUEUE_LIST QMGR_QUEUE_LIST; typedef struct QMGR_ENTRY_LIST QMGR_ENTRY_LIST; @@ -121,20 +121,27 @@ struct QMGR_TRANSPORT { int dest_concurrency_limit; /* concurrency per domain */ int init_dest_concurrency; /* init. per-domain concurrency */ int recipient_limit; /* recipients per transaction */ - int rcpt_per_stack; /* slots reserved for job on 1st stack level */ - int rcpt_unused; /* available in-core slots */ - int slot_cost; /* cost of new preemption slot (# selected entries) */ - int slot_loan; /* preemption boost offset and factor, see */ - int slot_loan_factor; /* qmgr_job_preempt() for more info */ - int min_slots; /* when preemption can take effect at all */ + int rcpt_per_stack; /* extra slots reserved for jobs on + * the job stack */ + int rcpt_unused; /* available in-core recipient slots */ + int slot_cost; /* cost of new preemption slot (# + * selected entries) */ + int slot_loan; /* preemption boost offset and + * factor, see */ + int slot_loan_factor; /* qmgr_job_preempt() for more info */ + int min_slots; /* when preemption can take effect at + * all */ struct HTABLE *queue_byname; /* queues indexed by domain */ QMGR_QUEUE_LIST queue_list; /* queues, round robin order */ - struct HTABLE *job_byname; /* jobs indexed by queue id */ - QMGR_JOB_LIST job_list; /* list of message jobs (1 per message) */ - QMGR_JOB_LIST job_stack; /* job stack for tracking preemption */ - QMGR_JOB *job_next_unread; /* next job with unread recipients */ - QMGR_JOB *candidate_cache; /* cached result from qmgr_job_candidate() */ - time_t candidate_cache_time; /* when candidate_cache was last updated */ + struct HTABLE *job_byname; /* jobs indexed by queue id */ + QMGR_JOB_LIST job_list; /* list of message jobs (1 per + * message) */ + QMGR_JOB_LIST job_stack; /* job stack for tracking preemption */ + QMGR_JOB *job_next_unread; /* next job with unread recipients */ + QMGR_JOB *candidate_cache; /* cached result from + * qmgr_job_candidate() */ + time_t candidate_cache_time; /* when candidate_cache was last + * updated */ QMGR_TRANSPORT_LIST peers; /* linkage */ char *reason; /* why unavailable */ }; @@ -217,7 +224,7 @@ struct QMGR_ENTRY { QMGR_MESSAGE *message; /* message info */ QMGR_RCPT_LIST rcpt_list; /* as many as it takes */ QMGR_QUEUE *queue; /* parent linkage */ - QMGR_PEER *peer; /* parent linkage */ + QMGR_PEER *peer; /* parent linkage */ QMGR_ENTRY_LIST queue_peers; /* per queue neighbor entries */ QMGR_ENTRY_LIST peer_peers; /* per peer neighbor entries */ }; @@ -240,7 +247,8 @@ struct QMGR_MESSAGE { int refcount; /* queue entries */ int single_rcpt; /* send one rcpt at a time */ long arrival_time; /* time when queued */ - time_t queued_time; /* time when moved to the active queue */ + time_t queued_time; /* time when moved to the active + * queue */ long warn_offset; /* warning bounce flag offset */ time_t warn_time; /* time next warning to be sent */ long data_offset; /* data seek offset */ @@ -254,10 +262,11 @@ struct QMGR_MESSAGE { long rcpt_offset; /* more recipients here */ long unread_offset; /* more unread recipients here */ QMGR_RCPT_LIST rcpt_list; /* complete addresses */ - int rcpt_count; /* used recipient slots */ - int rcpt_limit; /* maximum read in-core */ - int rcpt_unread; /* # of recipients left in queue file */ - QMGR_JOB_LIST job_list; /* jobs delivering this message (1 per transport) */ + int rcpt_count; /* used recipient slots */ + int rcpt_limit; /* maximum read in-core */ + int rcpt_unread; /* # of recipients left in queue file */ + QMGR_JOB_LIST job_list; /* jobs delivering this message (1 + * per transport) */ }; #define QMGR_MESSAGE_LOCKED ((QMGR_MESSAGE *) 1) @@ -274,39 +283,44 @@ extern QMGR_MESSAGE *qmgr_message_realloc(QMGR_MESSAGE *); /* * Sometimes it's required to access the transport queues and entries on per - * message basis. That's what the QMGR_JOB structure is for - it groups all per - * message information within each transport using a list of QMGR_PEER structures. - * These structures in turn correspond with per message QMGR_QUEUE structure - * and list all per message QMGR_ENTRY structures. + * message basis. That's what the QMGR_JOB structure is for - it groups all + * per message information within each transport using a list of QMGR_PEER + * structures. These structures in turn correspond with per message + * QMGR_QUEUE structure and list all per message QMGR_ENTRY structures. */ struct QMGR_PEER_LIST { - QMGR_PEER *next; - QMGR_PEER *prev; + QMGR_PEER *next; + QMGR_PEER *prev; }; struct QMGR_JOB { - QMGR_MESSAGE *message; /* message delivered by this job */ - QMGR_TRANSPORT *transport; /* transport this job belongs to */ - QMGR_JOB_LIST message_peers; /* per message neighbor linkage */ - QMGR_JOB_LIST transport_peers; /* per transport neighbor linkage */ - QMGR_JOB_LIST stack_peers; /* transport stack linkage */ - int stack_level; /* job stack nesting level (-1 -> retired) */ - struct HTABLE *peer_byname; /* message job peers, indexed by domain */ - QMGR_PEER_LIST peer_list; /* list of message job peers */ - int slots_used; /* slots used during preemption */ - int slots_available; /* slots available for preemption (* slot_cost) */ - int selected_entries; /* # of entries selected for delivery so far */ - int read_entries; /* # of entries read in-core so far */ - int rcpt_count; /* used recipient slots */ - int rcpt_limit; /* available recipient slots */ + QMGR_MESSAGE *message; /* message delivered by this job */ + QMGR_TRANSPORT *transport; /* transport this job belongs to */ + QMGR_JOB_LIST message_peers; /* per message neighbor linkage */ + QMGR_JOB_LIST transport_peers; /* per transport neighbor linkage */ + QMGR_JOB_LIST stack_peers; /* transport stack linkage */ + int stack_level; /* job stack nesting level (-1 -> + * retired) */ + struct HTABLE *peer_byname; /* message job peers, indexed by + * domain */ + QMGR_PEER_LIST peer_list; /* list of message job peers */ + int slots_used; /* slots used during preemption */ + int slots_available; /* slots available for preemption (* + * slot_cost) */ + int selected_entries; /* # of entries selected for delivery + * so far */ + int read_entries; /* # of entries read in-core so far */ + int rcpt_count; /* used recipient slots */ + int rcpt_limit; /* available recipient slots */ }; struct QMGR_PEER { - QMGR_JOB *job; /* job handling this peer */ - QMGR_QUEUE *queue; /* queue corresponding with this peer */ - int refcount; /* peer entries */ - QMGR_ENTRY_LIST entry_list; /* todo message entries queued for this peer */ - QMGR_PEER_LIST peers; /* neighbor linkage */ + QMGR_JOB *job; /* job handling this peer */ + QMGR_QUEUE *queue; /* queue corresponding with this peer */ + int refcount; /* peer entries */ + QMGR_ENTRY_LIST entry_list; /* todo message entries queued for + * this peer */ + QMGR_PEER_LIST peers; /* neighbor linkage */ }; extern QMGR_ENTRY *qmgr_job_entry_select(QMGR_TRANSPORT *); diff --git a/postfix/nqmgr/qmgr_entry.c b/postfix/nqmgr/qmgr_entry.c index 57b4631ec..9df7e74d8 100644 --- a/postfix/nqmgr/qmgr_entry.c +++ b/postfix/nqmgr/qmgr_entry.c @@ -94,19 +94,19 @@ /* qmgr_entry_select - select queue entry for delivery */ -QMGR_ENTRY *qmgr_entry_select(QMGR_PEER *peer) +QMGR_ENTRY *qmgr_entry_select(QMGR_PEER * peer) { QMGR_ENTRY *entry; QMGR_QUEUE *queue; if ((entry = peer->entry_list.next) != 0) { - queue = entry->queue; + queue = entry->queue; QMGR_LIST_UNLINK(queue->todo, QMGR_ENTRY *, entry, queue_peers); queue->todo_refcount--; QMGR_LIST_APPEND(queue->busy, entry, queue_peers); queue->busy_refcount++; QMGR_LIST_UNLINK(peer->entry_list, QMGR_ENTRY *, entry, peer_peers); - peer->job->selected_entries++; + peer->job->selected_entries++; } return (entry); } @@ -133,7 +133,8 @@ void qmgr_entry_done(QMGR_ENTRY *entry, int which) QMGR_QUEUE *queue = entry->queue; QMGR_MESSAGE *message = entry->message; QMGR_PEER *peer = entry->peer; - QMGR_JOB *sponsor, *job = peer->job; + QMGR_JOB *sponsor, + *job = peer->job; /* * Take this entry off the in-core queue. @@ -145,6 +146,7 @@ void qmgr_entry_done(QMGR_ENTRY *entry, int which) queue->busy_refcount--; } else if (which == QMGR_QUEUE_TODO) { QMGR_LIST_UNLINK(peer->entry_list, QMGR_ENTRY *, entry, peer_peers); + job->selected_entries++; QMGR_LIST_UNLINK(queue->todo, QMGR_ENTRY *, entry, queue_peers); queue->todo_refcount--; } else { @@ -162,27 +164,28 @@ void qmgr_entry_done(QMGR_ENTRY *entry, int which) myfree((char *) entry); /* - * Make sure that transport of any retired or finishing jobs that - * donated recipient slots to this job's message gets them back first. - * Then, if possible, pass the remaining unused recipient slots to the - * next job in the queue. + * Make sure that the transport of any retired or finishing job that + * donated recipient slots to this message gets them back first. Then, if + * possible, pass the remaining unused recipient slots to the next job in + * the job list. */ for (sponsor = message->job_list.next; sponsor; sponsor = sponsor->message_peers.next) { - if (sponsor->rcpt_count >= sponsor->rcpt_limit || sponsor == job) - continue; - if (sponsor->stack_level < 0 || message->rcpt_offset == 0) - qmgr_job_move_limits(sponsor); + if (sponsor->rcpt_count >= sponsor->rcpt_limit || sponsor == job) + continue; + if (sponsor->stack_level < 0 || message->rcpt_offset == 0) + qmgr_job_move_limits(sponsor); } if (message->rcpt_offset == 0) { - qmgr_job_move_limits(job); + qmgr_job_move_limits(job); } - + /* - * When there are no more entries for this peer, discard the peer structure. + * When there are no more entries for this peer, discard the peer + * structure. */ - peer->refcount--; - if (peer->refcount == 0) - qmgr_peer_free(peer); + peer->refcount--; + if (peer->refcount == 0) + qmgr_peer_free(peer); /* * When the in-core queue for this site is empty and when this site is @@ -208,7 +211,7 @@ void qmgr_entry_done(QMGR_ENTRY *entry, int which) /* qmgr_entry_create - create queue todo entry */ -QMGR_ENTRY *qmgr_entry_create(QMGR_PEER *peer, QMGR_MESSAGE *message) +QMGR_ENTRY *qmgr_entry_create(QMGR_PEER * peer, QMGR_MESSAGE *message) { QMGR_ENTRY *entry; QMGR_QUEUE *queue = peer->queue; @@ -226,7 +229,7 @@ QMGR_ENTRY *qmgr_entry_create(QMGR_PEER *peer, QMGR_MESSAGE *message) message->refcount++; entry->peer = peer; QMGR_LIST_APPEND(peer->entry_list, entry, peer_peers); - peer->refcount++ ; + peer->refcount++; entry->queue = queue; QMGR_LIST_APPEND(queue->todo, entry, queue_peers); queue->todo_refcount++; diff --git a/postfix/nqmgr/qmgr_job.c b/postfix/nqmgr/qmgr_job.c index 46bb8ee5d..d13c94912 100644 --- a/postfix/nqmgr/qmgr_job.c +++ b/postfix/nqmgr/qmgr_job.c @@ -26,8 +26,8 @@ /* /* qmgr_job_obtain() finds an existing job for named message and /* transport combination. New empty job is created if no existing can -/* be found.In either case, the job is prepared for assignement of -/* (more) message recipients +/* be found. In either case, the job is prepared for assignement of +/* (more) message recipients. /* /* qmgr_job_free() disposes of a per-transport job after all /* its entries have been taken care of. It is an error to dispose @@ -93,7 +93,7 @@ static void qmgr_job_pop(QMGR_JOB *); static QMGR_JOB *qmgr_job_create(QMGR_MESSAGE *message, QMGR_TRANSPORT *transport) { QMGR_JOB *job; - + job = (QMGR_JOB *) mymalloc(sizeof(QMGR_JOB)); job->message = message; QMGR_LIST_APPEND(message->job_list, job, message_peers); @@ -114,75 +114,78 @@ static QMGR_JOB *qmgr_job_create(QMGR_MESSAGE *message, QMGR_TRANSPORT *transpor /* qmgr_job_link - append the job to the job list, according to the time it was queued */ -static void qmgr_job_link(QMGR_JOB *job) +static void qmgr_job_link(QMGR_JOB * job) { QMGR_TRANSPORT *transport = job->transport; QMGR_MESSAGE *message = job->message; - QMGR_JOB *prev,*next,*unread; - int delay; - + QMGR_JOB *prev, + *next, + *unread; + int delay; + unread = transport->job_next_unread; - + /* * This may look inefficient but under normal operation it is expected - * that the loop will stop right away, resulting in normal list append below. - * However, this code is necessary for reviving retired jobs and for jobs - * which are created long after the first chunk of recipients was read in-core - * (either of these can happen only for multi-transport messages). - * - * In case this is found unsatisfactory one day, it's possible to deploy some - * smarter technique (using some form of lookup trees perhaps). + * that the loop will stop right away, resulting in normal list append + * below. However, this code is necessary for reviving retired jobs and + * for jobs which are created long after the first chunk of recipients + * was read in-core (either of these can happen only for multi-transport + * messages). + * + * In case this is found unsatisfactory one day, it's possible to deploy + * some smarter technique (using some form of lookup trees perhaps). */ for (next = 0, prev = transport->job_list.prev; prev; - next = prev, prev = prev->transport_peers.prev) - { - delay = message->queued_time - prev->message->queued_time; - if (delay >= 0) - break; - if (unread == prev) - unread = 0; + next = prev, prev = prev->transport_peers.prev) { + delay = message->queued_time - prev->message->queued_time; + if (delay >= 0) + break; + if (unread == prev) + unread = 0; } - + /* - * Don't link the new job in front of the first job on the job list - * if that job was already used for the regular delivery. - * This seems like a subtle difference but it helps many invariants - * used at various other places to remain true. + * Don't link the new job in front of the first job on the job list if + * that job was already used for the regular delivery. This seems like a + * subtle difference but it helps many invariants used at various other + * places to remain true. */ if (prev == 0 && next != 0 && next->slots_used != 0) { - prev = next; - next = next->transport_peers.next; - /* - * The following is not currently necessary but is done anyway - * for the sake of consistency. - */ - if (prev == transport->job_next_unread) - unread = prev; + prev = next; + next = next->transport_peers.next; + + /* + * The following is not currently necessary but is done anyway for + * the sake of consistency. + */ + if (prev == transport->job_next_unread) + unread = prev; } - + /* * Link the job into the proper place on the job list. */ job->transport_peers.prev = prev; job->transport_peers.next = next; if (prev != 0) - prev->transport_peers.next = job; + prev->transport_peers.next = job; else - transport->job_list.next = job; + transport->job_list.next = job; if (next != 0) - next->transport_peers.prev = job; + next->transport_peers.prev = job; else - transport->job_list.prev = job; + transport->job_list.prev = job; /* - * Update the pointer to the first unread job on the job list - * and steal the unused recipient slots from the old one. + * Update the pointer to the first unread job on the job list and steal + * the unused recipient slots from the old one. */ if (unread == 0) { - unread = transport->job_next_unread; - transport->job_next_unread = job; - if (unread != 0) - qmgr_job_move_limits(unread); + unread = transport->job_next_unread; + transport->job_next_unread = job; + if (unread != 0) + qmgr_job_move_limits(unread); } /* @@ -191,9 +194,9 @@ static void qmgr_job_link(QMGR_JOB *job) * (which is usually after all recipients have been read in core). */ if (transport->rcpt_unused > 0) { - job->rcpt_limit += transport->rcpt_unused; - message->rcpt_limit += transport->rcpt_unused; - transport->rcpt_unused = 0; + job->rcpt_limit += transport->rcpt_unused; + message->rcpt_limit += transport->rcpt_unused; + transport->rcpt_unused = 0; } } @@ -201,61 +204,63 @@ static void qmgr_job_link(QMGR_JOB *job) static QMGR_JOB *qmgr_job_find(QMGR_MESSAGE *message, QMGR_TRANSPORT *transport) { + /* - * Instead of traversing the message job list, we use single per transport - * hash table. This is better (at least with respect to memory usage) - * than having single hash table (usually almost empty) for each message. + * Instead of traversing the message job list, we use single per + * transport hash table. This is better (at least with respect to memory + * usage) than having single hash table (usually almost empty) for each + * message. */ return ((QMGR_JOB *) htable_find(transport->job_byname, message->queue_id)); } /* qmgr_job_obtain - find/create the appropriate job and make it ready for new recipients */ -QMGR_JOB * qmgr_job_obtain(QMGR_MESSAGE *message, QMGR_TRANSPORT *transport) +QMGR_JOB *qmgr_job_obtain(QMGR_MESSAGE *message, QMGR_TRANSPORT *transport) { QMGR_JOB *job; - + /* - * Try finding the existing job and revive it if it was already retired. - * Create the new job for this transport/message combination otherwise. + * Try finding an existing job and revive it if it was already retired. + * Create a new job for this transport/message combination otherwise. */ if ((job = qmgr_job_find(message, transport)) != 0) { - if (job->stack_level < 0) { - job->stack_level = 0; - qmgr_job_link(job); - } - } - else { - job = qmgr_job_create(message, transport); - qmgr_job_link(job); + if (job->stack_level < 0) { + job->stack_level = 0; + qmgr_job_link(job); + } + } else { + job = qmgr_job_create(message, transport); + qmgr_job_link(job); } /* * Reset the candidate cache because of the new expected recipients. - */ + */ RESET_CANDIDATE_CACHE(transport); - return(job); + return (job); } /* qmgr_job_move_limits - move unused recipient slots to the next job */ -void qmgr_job_move_limits(QMGR_JOB *job) +void qmgr_job_move_limits(QMGR_JOB * job) { QMGR_TRANSPORT *transport = job->transport; QMGR_MESSAGE *message = job->message; QMGR_JOB *next = transport->job_next_unread; - int rcpt_unused, msg_rcpt_unused; + int rcpt_unused, + msg_rcpt_unused; /* * Find next unread job on the job list if necessary. Cache it for later. * This makes the amortized efficiency of this routine O(1) per job. */ if (job == next) { - for (next = next->transport_peers.next; next; next = next->transport_peers.next) - if (next->message->rcpt_offset != 0) - break; - transport->job_next_unread = next; + for (next = next->transport_peers.next; next; next = next->transport_peers.next) + if (next->message->rcpt_offset != 0) + break; + transport->job_next_unread = next; } /* @@ -263,54 +268,55 @@ void qmgr_job_move_limits(QMGR_JOB *job) */ rcpt_unused = job->rcpt_limit - job->rcpt_count; msg_rcpt_unused = message->rcpt_limit - message->rcpt_count; - if( msg_rcpt_unused < rcpt_unused ) - rcpt_unused = msg_rcpt_unused; + if (msg_rcpt_unused < rcpt_unused) + rcpt_unused = msg_rcpt_unused; /* - * Transfer the unused recipient slots back to the transport pool and - * to the next not-fully-read job. Job's message limits are adjusted accordingly. + * Transfer the unused recipient slots back to the transport pool and to + * the next not-fully-read job. Job's message limits are adjusted + * accordingly. */ if (rcpt_unused > 0) { - job->rcpt_limit -= rcpt_unused; - message->rcpt_limit -= rcpt_unused; - transport->rcpt_unused += rcpt_unused; - if (next != 0 && (rcpt_unused = transport->rcpt_unused) > 0) { - next->rcpt_limit += rcpt_unused; - next->message->rcpt_limit += rcpt_unused; - transport->rcpt_unused = 0; - } + job->rcpt_limit -= rcpt_unused; + message->rcpt_limit -= rcpt_unused; + transport->rcpt_unused += rcpt_unused; + if (next != 0 && (rcpt_unused = transport->rcpt_unused) > 0) { + next->rcpt_limit += rcpt_unused; + next->message->rcpt_limit += rcpt_unused; + transport->rcpt_unused = 0; + } } } /* qmgr_job_retire - remove the job from the job list while waiting for recipients to deliver */ -static void qmgr_job_retire(QMGR_JOB *job) +static void qmgr_job_retire(QMGR_JOB * job) { - char *myname = "qmgr_job_retire"; + char *myname = "qmgr_job_retire"; QMGR_TRANSPORT *transport = job->transport; if (msg_verbose) - msg_info("%s: %s", myname, job->message->queue_id); + msg_info("%s: %s", myname, job->message->queue_id); /* * Sanity checks. */ if (job->stack_level != 0) - msg_panic("%s: non-zero stack level (%d)", myname, job->stack_level); + msg_panic("%s: non-zero stack level (%d)", myname, job->stack_level); /* - * Make sure this job is not cached as the next unread job for this transport. - * The qmgr_entry_done() will make sure that the slots donated by this job - * are moved back to the transport pool as soon as possible. + * Make sure this job is not cached as the next unread job for this + * transport. The qmgr_entry_done() will make sure that the slots donated + * by this job are moved back to the transport pool as soon as possible. */ qmgr_job_move_limits(job); - + /* * Invalidate the candidate selection cache if necessary. */ if (job == transport->candidate_cache - || (transport->job_stack.next == 0 && job == transport->job_list.next)) - RESET_CANDIDATE_CACHE(transport); + || (transport->job_stack.next == 0 && job == transport->job_list.next)) + RESET_CANDIDATE_CACHE(transport); /* * Remove the job from the job list and mark it as retired. @@ -321,144 +327,153 @@ static void qmgr_job_retire(QMGR_JOB *job) /* qmgr_job_free - release the job structure */ -void qmgr_job_free(QMGR_JOB *job) +void qmgr_job_free(QMGR_JOB * job) { - char *myname = "qmgr_job_free"; + char *myname = "qmgr_job_free"; QMGR_MESSAGE *message = job->message; QMGR_TRANSPORT *transport = job->transport; if (msg_verbose) - msg_info("%s: %s %s", myname, message->queue_id, transport->name); + msg_info("%s: %s %s", myname, message->queue_id, transport->name); /* * Sanity checks. */ if (job->rcpt_count) - msg_panic("%s: non-zero recipient count (%d)", myname, job->rcpt_count); + msg_panic("%s: non-zero recipient count (%d)", myname, job->rcpt_count); /* * Remove the job from the job stack if necessary. */ if (job->stack_level > 0) - qmgr_job_pop(job); + qmgr_job_pop(job); /* * Return any remaining recipient slots back to the recipient slots pool. */ qmgr_job_move_limits(job); if (job->rcpt_limit) - msg_panic("%s: recipient slots leak (%d)", myname, job->rcpt_limit); - + msg_panic("%s: recipient slots leak (%d)", myname, job->rcpt_limit); + /* * Invalidate the candidate selection cache if necessary. */ if (job == transport->candidate_cache - || (transport->job_stack.next == 0 && job == transport->job_list.next)) - RESET_CANDIDATE_CACHE(transport); + || (transport->job_stack.next == 0 && job == transport->job_list.next)) + RESET_CANDIDATE_CACHE(transport); /* - * Unlink and discard the structure. Check if the job is still on the transport - * job list or if it was already retired before unlinking it. + * Unlink and discard the structure. Check if the job is still on the + * transport job list or if it was already retired before unlinking it. */ if (job->stack_level >= 0) - QMGR_LIST_UNLINK(transport->job_list, QMGR_JOB *, job, transport_peers); + QMGR_LIST_UNLINK(transport->job_list, QMGR_JOB *, job, transport_peers); QMGR_LIST_UNLINK(message->job_list, QMGR_JOB *, job, message_peers); - htable_delete(transport->job_byname, message->queue_id, (void (*) (char *)) 0) ; + htable_delete(transport->job_byname, message->queue_id, (void (*) (char *)) 0); htable_free(job->peer_byname, (void (*) (char *)) 0); myfree((char *) job); } -/* qmgr_job_count_slots - maintain the delivery slots' counters */ +/* qmgr_job_count_slots - maintain the delivery slot counters */ -static void qmgr_job_count_slots(QMGR_JOB *current, QMGR_JOB *job) +static void qmgr_job_count_slots(QMGR_JOB * current, QMGR_JOB * job) { + /* - * Count the number of delivery slots used during the delivery - * of the selected job and also the number of delivery slots - * available for preemption. - * + * Count the number of delivery slots used during the delivery of the + * selected job. Also count the number of delivery slots available for + * preemption. + * * However, suppress any slot counting if we didn't start regular delivery * of the selected job yet. */ if (job == current || job->slots_used > 0) { - job->slots_used++; - job->slots_available++; + job->slots_used++; + job->slots_available++; } - + /* * If the selected job is not the current job, its chance to be chosen by * qmgr_job_candidate() has slightly changed. If we would like to make - * the candidate cache completely transparent, we should invalidate it now. - * + * the candidate cache completely transparent, we should invalidate it + * now. + * * However, this case should usually happen only at "end of current job" * phase, when it's unlikely that the current job can be preempted * anyway. And because it's likely to happen quite often then, we - * intentionally don't reset the cache, to safe some cycles. - * Furthermore, the cache times out every second anyway. + * intentionally don't reset the cache, to safe some cycles. Furthermore, + * the cache times out every second anyway. */ #if 0 if (job != current) - RESET_CANDIDATE_CACHE(job->transport); + RESET_CANDIDATE_CACHE(job->transport); #endif } /* qmgr_job_candidate - find best job candidate for preempting given job */ -static QMGR_JOB *qmgr_job_candidate(QMGR_JOB *current) +static QMGR_JOB *qmgr_job_candidate(QMGR_JOB * current) { QMGR_TRANSPORT *transport = current->transport; - QMGR_JOB *job, *best_job = 0; - float score, best_score = 0.0; - int max_slots, max_needed_entries, max_total_entries; - int delay; - time_t now = event_time(); - + QMGR_JOB *job, + *best_job = 0; + float score, + best_score = 0.0; + int max_slots, + max_needed_entries, + max_total_entries; + int delay; + time_t now = event_time(); + /* * Fetch the result directly from the cache if the cache is still valid. - * - * Note that we cache negative results too, so the cache must be - * invalidated by resetting the cache time, not the candidate pointer itself. + * + * Note that we cache negative results too, so the cache must be invalidated + * by resetting the cache time, not the candidate pointer itself. */ if (transport->candidate_cache_time == now) - return (transport->candidate_cache); - + return (transport->candidate_cache); + /* - * Estimate the minimum amount of delivery slots that can ever be accumulated - * for the given job. All jobs that won't fit into these slots are excluded - * from the candidate selection. + * Estimate the minimum amount of delivery slots that can ever be + * accumulated for the given job. All jobs that won't fit into these + * slots are excluded from the candidate selection. */ max_slots = (MIN_ENTRIES(current) - current->selected_entries - + current->slots_available) / transport->slot_cost; + + current->slots_available) / transport->slot_cost; /* - * Select the candidate with best time_since_queued/total_recipients score. - * In addition to jobs which don't meet the max_slots limit, skip also jobs - * which don't have any selectable entries at the moment. - * - * By the way, the selection is reasonably resistant to OS time warping, too. - * - * However, don't bother searching if we can't find anything suitable anyway. + * Select the candidate with best time_since_queued/total_recipients + * score. In addition to jobs which don't meet the max_slots limit, skip + * also jobs which don't have any selectable entries at the moment. + * + * By the way, the selection is reasonably resistant to OS time warping, + * too. + * + * However, don't bother searching if we can't find anything suitable + * anyway. */ if (max_slots > 0) { - for (job = transport->job_list.next; job; job = job->transport_peers.next) { - if (job->stack_level != 0 || job == current) - continue; - max_total_entries = MAX_ENTRIES(job); - max_needed_entries = max_total_entries - job->selected_entries; - delay = now - job->message->queued_time + 1; - if (max_needed_entries > 0 && max_needed_entries <= max_slots) { - score = (float) delay / max_total_entries; - if (score > best_score) { - best_score = score; - best_job = job; - } - } - /* - * Stop early if the best score is as good as it can get. - */ - if (delay <= best_score) - break; - } + for (job = transport->job_list.next; job; job = job->transport_peers.next) { + if (job->stack_level != 0 || job == current) + continue; + max_total_entries = MAX_ENTRIES(job); + max_needed_entries = max_total_entries - job->selected_entries; + delay = now - job->message->queued_time + 1; + if (max_needed_entries > 0 && max_needed_entries <= max_slots) { + score = (float) delay / max_total_entries; + if (score > best_score) { + best_score = score; + best_job = job; + } + } + + /* + * Stop early if the best score is as good as it can get. + */ + if (delay <= best_score) + break; + } } /* @@ -472,33 +487,34 @@ static QMGR_JOB *qmgr_job_candidate(QMGR_JOB *current) /* qmgr_job_preempt - preempt large message with smaller one */ -static QMGR_JOB * qmgr_job_preempt(QMGR_JOB *current) +static QMGR_JOB *qmgr_job_preempt(QMGR_JOB * current) { - char *myname = "qmgr_job_preempt"; + char *myname = "qmgr_job_preempt"; QMGR_TRANSPORT *transport = current->transport; QMGR_JOB *job; - int rcpt_slots; - + int rcpt_slots; + /* - * Suppress preempting completely if the current job is not big enough - * to accumulate even the mimimal number of slots required. - * - * Also, don't look for better job candidate if there are no available - * slots yet (the count can get negative due to the slot loans below). + * Suppress preempting completely if the current job is not big enough to + * accumulate even the mimimal number of slots required. + * + * Also, don't look for better job candidate if there are no available slots + * yet (the count can get negative due to the slot loans below). */ if (current->slots_available <= 0 - || MAX_ENTRIES(current) < transport->min_slots * transport->slot_cost) - return (current); + || MAX_ENTRIES(current) < transport->min_slots * transport->slot_cost) + return (current); /* * Find best candidate for preempting the current job. - * - * Note that the function also takes care that the candidate fits within - * the number of delivery slots the current job can ever accumulate yet. - */ + * + * Note that the function also takes care that the candidate fits within the + * number of delivery slots which the current job is still able to + * accumulate. + */ if ((job = qmgr_job_candidate(current)) == 0) - return (current); - + return (current); + /* * Sanity checks. */ @@ -508,89 +524,90 @@ static QMGR_JOB * qmgr_job_preempt(QMGR_JOB *current) msg_panic("%s: already on the job stack (%d)", myname, job->stack_level); /* - * Check if there is enough available delivery slots accumulated - * to preempt the current job. - * - * The slot loaning scheme improves the average message response time. - * Note that the loan only allows the preemption happen earlier, though. - * It doesn't affect how many slots have to be "paid" - the full number - * of slots required will have to accumulate later before next - * preemption on the same stack level can happen anyway. + * Check if there is enough available delivery slots accumulated to + * preempt the current job. + * + * The slot loaning scheme improves the average message response time. Note + * that the loan only allows the preemption happen earlier, though. It + * doesn't affect how many slots have to be "paid" - the full number of + * slots required has to be accumulated later before next preemption on + * the same stack level can happen in either case. */ - if (current->slots_available / transport->slot_cost - + transport->slot_loan - < (MAX_ENTRIES(job) - job->selected_entries) - * transport->slot_loan_factor / 100.0) - return (current); + if (current->slots_available / transport->slot_cost + + transport->slot_loan + < (MAX_ENTRIES(job) - job->selected_entries) + * transport->slot_loan_factor / 100.0) + return (current); /* * Preempt the current job. */ QMGR_LIST_PREPEND(transport->job_stack, job, stack_peers); job->stack_level = current->stack_level + 1; - + /* - * Add part of extra recipient slots reserved for preempting jobs - * to the new current job if necessary. - * - * Note that transport->rcpt_unused is within <-rcpt_per_stack,0> in such case. + * Add part of extra recipient slots reserved for preempting jobs to the + * new current job if necessary. + * + * Note that transport->rcpt_unused is within <-rcpt_per_stack,0> in such + * case. */ if (job->message->rcpt_offset != 0) { - rcpt_slots = (transport->rcpt_per_stack + transport->rcpt_unused + 1) / 2; - job->rcpt_limit += rcpt_slots; - job->message->rcpt_limit += rcpt_slots; - transport->rcpt_unused -= rcpt_slots; + rcpt_slots = (transport->rcpt_per_stack + transport->rcpt_unused + 1) / 2; + job->rcpt_limit += rcpt_slots; + job->message->rcpt_limit += rcpt_slots; + transport->rcpt_unused -= rcpt_slots; } /* - * Candidate cache must be reset because the current job has changed completely. + * Candidate cache must be reset because the current job has changed + * completely. */ RESET_CANDIDATE_CACHE(transport); if (msg_verbose) - msg_info("%s: %s by %s", myname, current->message->queue_id, - job->message->queue_id); + msg_info("%s: %s by %s", myname, current->message->queue_id, + job->message->queue_id); return (job); } /* qmgr_job_pop - remove the job from the job preemption stack */ -static void qmgr_job_pop(QMGR_JOB *job) +static void qmgr_job_pop(QMGR_JOB * job) { QMGR_TRANSPORT *transport = job->transport; QMGR_JOB *parent; - + if (msg_verbose) - msg_info("qmgr_job_pop: %s", job->message->queue_id); + msg_info("qmgr_job_pop: %s", job->message->queue_id); /* - * Adjust the number of delivery slots available to preempt - * job's parent. + * Adjust the number of delivery slots available to preempt job's parent. + * + * Note that we intentionally do not adjust slots_used of the parent. Doing + * so would decrease the maximum per message inflation factor if the + * preemption appeared near the end of parent delivery. + * + * For the same reason we do not adjust parent's slots_available if the + * parent is not the original parent preempted by the selected job (i.e., + * the original parent job has already completed). * - * Note that we intentionally do not adjust slots_used of the parent. - * Doing so would decrease the maximum per message inflation factor - * if the preemption appeared near the end of parent delivery. - * - * For the same reason we do not adjust parent's slots_available - * if the parent is not the original parent preempted by the - * selected job (i.e., the original parent job has already completed). - * - * The special case when the head of the job list was preempted and - * then delivered before the preempting job itself is taken care of too. + * The special case when the head of the job list was preempted and then + * delivered before the preempting job itself is taken care of too. * Otherwise we would decrease available slot counter of some job that * was not in fact preempted yet. */ if (((parent = job->stack_peers.next) != 0 - || ((parent = transport->job_list.next) != 0 && parent->slots_used > 0)) - && job->stack_level == parent->stack_level + 1) - parent->slots_available -= job->slots_used * transport->slot_cost; + || ((parent = transport->job_list.next) != 0 && parent->slots_used > 0)) + && job->stack_level == parent->stack_level + 1) + parent->slots_available -= job->slots_used * transport->slot_cost; /* * Invalidate the candidate selection cache if necessary. */ if (job == transport->job_stack.next) - RESET_CANDIDATE_CACHE(transport); + RESET_CANDIDATE_CACHE(transport); /* * Remove the job from the job stack and reinitialize the slot counters. @@ -603,13 +620,13 @@ static void qmgr_job_pop(QMGR_JOB *job) /* qmgr_job_peer_select - select next peer suitable for delivery */ -static QMGR_PEER *qmgr_job_peer_select(QMGR_JOB *job) +static QMGR_PEER *qmgr_job_peer_select(QMGR_JOB * job) { QMGR_PEER *peer; QMGR_MESSAGE *message = job->message; - + if (HAS_ENTRIES(job) && (peer = qmgr_peer_select(job)) != 0) - return (peer); + return (peer); /* * Try reading in more recipients. Note that we do not try to read them @@ -617,9 +634,9 @@ static QMGR_PEER *qmgr_job_peer_select(QMGR_JOB *job) * recipient grouping. We waited until reading more is really necessary. */ if (message->rcpt_offset != 0 && message->rcpt_limit > message->rcpt_count) { - qmgr_message_realloc(message); - if (HAS_ENTRIES(job)) - return (qmgr_peer_select(job)); + qmgr_message_realloc(message); + if (HAS_ENTRIES(job)) + return (qmgr_peer_select(job)); } return (0); } @@ -628,102 +645,104 @@ static QMGR_PEER *qmgr_job_peer_select(QMGR_JOB *job) QMGR_ENTRY *qmgr_job_entry_select(QMGR_TRANSPORT *transport) { - QMGR_JOB *job, *current, *next; + QMGR_JOB *job, + *current, + *next; QMGR_PEER *peer; QMGR_ENTRY *entry; - + /* * Select the "current" job. */ if ((current = transport->job_stack.next) == 0 - && (current = transport->job_list.next) == 0) - return (0); - + && (current = transport->job_list.next) == 0) + return (0); + /* * Exercise the preempting algorithm if enabled. - * + * * The slot_cost equal to 1 causes the algorithm to degenerate and is * therefore disabled too. */ if (transport->slot_cost >= 2) - current = qmgr_job_preempt(current); + current = qmgr_job_preempt(current); /* * Select next entry suitable for delivery. First check the stack of * preempting jobs, then the list of all remaining jobs in FIFO order. - * - * Note that although the loops may look inefficient, they only serve as - * a recovery mechanism when an entry of the current job itself can't be + * + * Note that although the loops may look inefficient, they only serve as a + * recovery mechanism when an entry of the current job itself can't be * selected due peer concurrency restrictions. In most cases some entry * of the current job itself is selected. - * + * * Note that both loops also take care of getting the "stall" current job * (job with no entries currently available) out of the way if necessary. - * Stall jobs can appear in case of multi-transport messages whose recipients - * don't fit in-core at once. Some jobs created by such message may have - * only few recipients and would block the job queue until all other - * jobs of the message are delivered. Trying to read in more recipients - * of such jobs each selection would also break the per peer recipient - * grouping of the other jobs. That's why we retire such jobs below. + * Stall jobs can appear in case of multi-transport messages whose + * recipients don't fit in-core at once. Some jobs created by such + * message may have only few recipients and would block the job queue + * until all other jobs of the message are delivered. Trying to read in + * more recipients of such jobs each selection would also break the per + * peer recipient grouping of the other jobs. That's why we retire such + * jobs below. */ for (job = transport->job_stack.next; job; job = next) { - next = job->stack_peers.next; - if ((peer = qmgr_job_peer_select(job)) != 0) { - entry = qmgr_entry_select(peer); - qmgr_job_count_slots(current, job); - - /* - * In case we selected the very last job entry, remove the job - * from the job stack and the job list right now. - * - * This action uses the assumption that once the job entry - * has been selected, it can be unselected only before the - * message ifself is deferred. Thus the job with all entries - * selected can't re-appear with more entries available for - * selection again (without reading in more entries from - * the queue file, which in turn invokes qmgr_job_obtain() - * which re-links the job back on the list if necessary). - * - * Note that qmgr_job_move_limits() transfers the recipients slots - * correctly even if the job is unlinked from the job list - * thanks to the job_next_unread caching. - */ - if (!HAS_ENTRIES(job) && job->message->rcpt_offset == 0) { - qmgr_job_pop(job); - qmgr_job_retire(job); - } - return (entry); - } - else if (job == current && !HAS_ENTRIES(job)) { - qmgr_job_pop(job); - qmgr_job_retire(job); - current = next ? next : transport->job_list.next; - } + next = job->stack_peers.next; + if ((peer = qmgr_job_peer_select(job)) != 0) { + entry = qmgr_entry_select(peer); + qmgr_job_count_slots(current, job); + + /* + * In case we selected the very last job entry, remove the job + * from the job stack and the job list right now. + * + * This action uses the assumption that once the job entry has been + * selected, it can be unselected only before the message ifself + * is deferred. Thus the job with all entries selected can't + * re-appear with more entries available for selection again + * (without reading in more entries from the queue file, which in + * turn invokes qmgr_job_obtain() which re-links the job back on + * the list if necessary). + * + * Note that qmgr_job_move_limits() transfers the recipients slots + * correctly even if the job is unlinked from the job list thanks + * to the job_next_unread caching. + */ + if (!HAS_ENTRIES(job) && job->message->rcpt_offset == 0) { + qmgr_job_pop(job); + qmgr_job_retire(job); + } + return (entry); + } else if (job == current && !HAS_ENTRIES(job)) { + qmgr_job_pop(job); + qmgr_job_retire(job); + current = next ? next : transport->job_list.next; + } } - + /* - * Try the regular job list if there is nothing (suitable) on the job stack. + * Try the regular job list if there is nothing (suitable) on the job + * stack. */ for (job = transport->job_list.next; job; job = next) { - next = job->transport_peers.next; - if (job->stack_level != 0) - continue; - if ((peer = qmgr_job_peer_select(job)) != 0) { - entry = qmgr_entry_select(peer); - qmgr_job_count_slots(current, job); - - /* - * In case we selected the very last job entry, remove the job - * from the job list right away. - */ - if (!HAS_ENTRIES(job) && job->message->rcpt_offset == 0) - qmgr_job_retire(job); - return (entry); - } - else if (job == current && !HAS_ENTRIES(job)) { - qmgr_job_retire(job); - current = next; - } + next = job->transport_peers.next; + if (job->stack_level != 0) + continue; + if ((peer = qmgr_job_peer_select(job)) != 0) { + entry = qmgr_entry_select(peer); + qmgr_job_count_slots(current, job); + + /* + * In case we selected the very last job entry, remove the job + * from the job list right away. + */ + if (!HAS_ENTRIES(job) && job->message->rcpt_offset == 0) + qmgr_job_retire(job); + return (entry); + } else if (job == current && !HAS_ENTRIES(job)) { + qmgr_job_retire(job); + current = next; + } } return (0); } diff --git a/postfix/nqmgr/qmgr_message.c b/postfix/nqmgr/qmgr_message.c index f40f75c50..7814ff632 100644 --- a/postfix/nqmgr/qmgr_message.c +++ b/postfix/nqmgr/qmgr_message.c @@ -51,7 +51,7 @@ /* structure members. A null result means that the file could not be /* read or that the file contained incorrect information. Recipient /* limit imposed this time is based on the position of the message -/* job(s) on corresponding job list(s). +/* job(s) on corresponding transport job list(s). /* /* qmgr_message_free() destroys an in-core message structure and makes /* the resources available for reuse. It is an error to destroy @@ -83,7 +83,7 @@ #include #include #include -#include /* sscanf() */ +#include /* sscanf() */ #ifdef STRCASECMP_IN_STRINGS_H #include @@ -197,34 +197,36 @@ static int qmgr_message_open(QMGR_MESSAGE *message) return (0); } -/* qmgr_message_oldstyle_scan - extract required information from old style queue file */ +/* qmgr_message_oldstyle_scan - extract required information from an old style queue file */ static void qmgr_message_oldstyle_scan(QMGR_MESSAGE *message) { VSTRING *buf; - long orig_offset, curr_offset, extra_offset; + long orig_offset, + curr_offset, + extra_offset; int rec_type; - char *start; + char *start; /* * Initialize. No early returns or we have a memory leak. */ buf = vstring_alloc(100); if ((orig_offset = vstream_ftell(message->fp)) < 0) - msg_fatal("vstream_ftell %s: %m", VSTREAM_PATH(message->fp)); + msg_fatal("vstream_ftell %s: %m", VSTREAM_PATH(message->fp)); /* - * Rewind to the very begining to make sure we see all records. + * Rewind to the very beginning to make sure we see all records. */ if (vstream_fseek(message->fp, 0, SEEK_SET) < 0) - msg_fatal("seek file %s: %m", VSTREAM_PATH(message->fp)); + msg_fatal("seek file %s: %m", VSTREAM_PATH(message->fp)); /* - * Scan through the old style queue file. Count the total number - * of recipients and find the data/extra sections offsets. - * Note that the new queue files require that data_size equals - * extra_offset - data_offset, so we set data_size to this as well - * and ignore the size record itself completely. + * Scan through the old style queue file. Count the total number of + * recipients and find the data/extra sections offsets. Note that the new + * queue files require that data_size equals extra_offset - data_offset, + * so we set data_size to this as well and ignore the size record itself + * completely. */ message->rcpt_unread = 0; do { @@ -239,18 +241,18 @@ static void qmgr_message_oldstyle_scan(QMGR_MESSAGE *message) msg_fatal("vstream_ftell %s: %m", VSTREAM_PATH(message->fp)); if ((extra_offset = atol(start)) <= curr_offset) msg_fatal("bad extra offset %s file %s", - start, VSTREAM_PATH(message->fp)); + start, VSTREAM_PATH(message->fp)); if (vstream_fseek(message->fp, extra_offset, SEEK_SET) < 0) msg_fatal("seek file %s: %m", VSTREAM_PATH(message->fp)); - message->data_size = extra_offset - message->data_offset; + message->data_size = extra_offset - message->data_offset; } } while (rec_type > 0 && rec_type != REC_TYPE_END); - + /* * Clean up. */ if (vstream_fseek(message->fp, orig_offset, SEEK_SET) < 0) - msg_fatal("seek file %s: %m", VSTREAM_PATH(message->fp)); + msg_fatal("seek file %s: %m", VSTREAM_PATH(message->fp)); vstring_free(buf); /* @@ -281,15 +283,15 @@ static int qmgr_message_read(QMGR_MESSAGE *message) /* * If we re-open this file, skip over on-file recipient records that we * already looked at, and reset the in-core recipient address list. - * - * For the first time, the message recipient limit is calculated from - * the global recipient limit. This is to avoid reading little recipients + * + * For the first time, the message recipient limit is calculated from the + * global recipient limit. This is to avoid reading little recipients * when the active queue is near empty. When the queue becomes full, only * the necessary amount is read in core. Such priming is necessary * because there are no message jobs yet. - * + * * For the next time, the recipient limit is based solely on the message - * jobs' positions in the job queues and/or job stacks. + * jobs' positions in the job lists and/or job stacks. */ if (message->rcpt_offset) { if (message->rcpt_list.len) @@ -298,11 +300,10 @@ static int qmgr_message_read(QMGR_MESSAGE *message) msg_fatal("seek file %s: %m", VSTREAM_PATH(message->fp)); message->rcpt_offset = 0; recipient_limit = message->rcpt_limit - message->rcpt_count; - } - else { - recipient_limit = var_qmgr_rcpt_limit - qmgr_recipient_count; - if (recipient_limit < message->rcpt_limit) - recipient_limit = message->rcpt_limit; + } else { + recipient_limit = var_qmgr_rcpt_limit - qmgr_recipient_count; + if (recipient_limit < message->rcpt_limit) + recipient_limit = message->rcpt_limit; } /* @@ -312,49 +313,50 @@ static int qmgr_message_read(QMGR_MESSAGE *message) * may appear before or after the message content, so we keep reading * from the queue file until we have enough recipients (rcpt_offset != 0) * and until we know where the message content starts (data_offset != 0). - * + * * Note that the total recipient count record is accurate only for fresh - * queue files. After some of the recipients are marked as done and - * the queue file is deferred, it can be used as upper bound estimate - * only. Fortunately, this poses no major problem on the scheduling - * algorithm, as the only impact is that the already deferred messages - * are not chosen by qmgr_job_candidate() as often as they could. + * queue files. After some of the recipients are marked as done and the + * queue file is deferred, it can be used as upper bound estimate only. + * Fortunately, this poses no major problem on the scheduling algorithm, + * as the only impact is that the already deferred messages are not + * chosen by qmgr_job_candidate() as often as they could. */ do { if ((curr_offset = vstream_ftell(message->fp)) < 0) msg_fatal("vstream_ftell %s: %m", VSTREAM_PATH(message->fp)); - if (curr_offset == message->data_offset && curr_offset > 0) { - extra_offset = curr_offset + message->data_size; - if (extra_offset <= curr_offset) + if (curr_offset == message->data_offset && curr_offset > 0) { + extra_offset = curr_offset + message->data_size; + if (extra_offset <= curr_offset) msg_fatal("bad extra offset %ld file %s", - extra_offset, VSTREAM_PATH(message->fp)); - if (vstream_fseek(message->fp, extra_offset, SEEK_SET) < 0) - msg_fatal("seek file %s: %m", VSTREAM_PATH(message->fp)); - curr_offset = extra_offset; - } + extra_offset, VSTREAM_PATH(message->fp)); + if (vstream_fseek(message->fp, extra_offset, SEEK_SET) < 0) + msg_fatal("seek file %s: %m", VSTREAM_PATH(message->fp)); + curr_offset = extra_offset; + } rec_type = rec_get(message->fp, buf, 0); start = vstring_str(buf); if (rec_type == REC_TYPE_SIZE) { if (message->data_size == 0) { - switch (sscanf(start, "%ld %ld %d", &message->data_size, - &message->data_offset, &message->rcpt_unread)) - { - case 1: - /* - * Gather data_size, data_offset and rcpt_unread values - * from the old style queue file. - */ - qmgr_message_oldstyle_scan(message); - break; - case 3: - /* - * No extra work for new style queue files. - */ - break; - default: - msg_fatal("%s: weird size record", message->queue_id); - break; - } + switch (sscanf(start, "%ld %ld %d", &message->data_size, + &message->data_offset, &message->rcpt_unread)) { + case 1: + + /* + * Gather data_size, data_offset and rcpt_unread values + * from the old style queue file. + */ + qmgr_message_oldstyle_scan(message); + break; + case 3: + + /* + * No extra work for new style queue files. + */ + break; + default: + msg_fatal("%s: weird size record", message->queue_id); + break; + } } } else if (rec_type == REC_TYPE_TIME) { if (message->arrival_time == 0) @@ -366,12 +368,13 @@ static int qmgr_message_read(QMGR_MESSAGE *message) if (message->sender == 0) { message->sender = mystrdup(start); opened(message->queue_id, message->sender, - message->data_size, "queue %s", message->queue_name); + message->data_size, message->rcpt_unread, + "queue %s", message->queue_name); } } else if (rec_type == REC_TYPE_DONE) { if (curr_offset > message->unread_offset) { - message->unread_offset = curr_offset; - message->rcpt_unread--; + message->unread_offset = curr_offset; + message->rcpt_unread--; } } else if (rec_type == REC_TYPE_RCPT) { if (message->rcpt_list.len < recipient_limit) { @@ -432,9 +435,9 @@ static int qmgr_message_read(QMGR_MESSAGE *message) */ if (message->rcpt_unread < 0 || (message->rcpt_offset == 0 && message->rcpt_unread != 0)) { - msg_warn("%s: rcpt count mismatch (%d)", - message->queue_id, message->rcpt_unread); - message->rcpt_unread = 0; + msg_warn("%s: rcpt count mismatch (%d)", + message->queue_id, message->rcpt_unread); + message->rcpt_unread = 0; } if (message->arrival_time == 0 || message->sender == 0 @@ -777,13 +780,12 @@ static void qmgr_message_assign(QMGR_MESSAGE *message) || !LIMIT_OK(queue->transport->recipient_limit, entry->rcpt_list.len)) { if (job == 0 || queue->transport != job->transport) { - /* FIXME: randomly rotate the peer list of previous job */ job = qmgr_job_obtain(message, queue->transport); peer = 0; } if (peer == 0 || queue != peer->queue) { if ((peer = qmgr_peer_find(job, queue)) == 0) - peer = qmgr_peer_create(job, queue); + peer = qmgr_peer_create(job, queue); } entry = qmgr_entry_create(peer, message); job->read_entries++; @@ -803,9 +805,9 @@ static void qmgr_message_assign(QMGR_MESSAGE *message) static void qmgr_message_move_limits(QMGR_MESSAGE *message) { QMGR_JOB *job; - + for (job = message->job_list.next; job; job = job->message_peers.next) - qmgr_job_move_limits(job); + qmgr_job_move_limits(job); } /* qmgr_message_free - release memory for in-core message structure */ @@ -813,12 +815,13 @@ static void qmgr_message_move_limits(QMGR_MESSAGE *message) void qmgr_message_free(QMGR_MESSAGE *message) { QMGR_JOB *job; + if (message->refcount != 0) msg_panic("qmgr_message_free: reference len: %d", message->refcount); if (message->fp) msg_panic("qmgr_message_free: queue file is open"); while ((job = message->job_list.next) != 0) - qmgr_job_free(job); + qmgr_job_free(job); myfree(message->queue_id); myfree(message->queue_name); if (message->sender) @@ -887,8 +890,8 @@ QMGR_MESSAGE *qmgr_message_alloc(const char *queue_name, const char *queue_id, qmgr_message_sort(message); qmgr_message_assign(message); qmgr_message_close(message); - if(message->rcpt_offset == 0) - qmgr_message_move_limits(message); + if (message->rcpt_offset == 0) + qmgr_message_move_limits(message); return (message); } } @@ -923,8 +926,8 @@ QMGR_MESSAGE *qmgr_message_realloc(QMGR_MESSAGE *message) qmgr_message_sort(message); qmgr_message_assign(message); qmgr_message_close(message); - if(message->rcpt_offset == 0) - qmgr_message_move_limits(message); + if (message->rcpt_offset == 0) + qmgr_message_move_limits(message); return (message); } } diff --git a/postfix/nqmgr/qmgr_peer.c b/postfix/nqmgr/qmgr_peer.c index 72953d3a4..b445a6793 100644 --- a/postfix/nqmgr/qmgr_peer.c +++ b/postfix/nqmgr/qmgr_peer.c @@ -28,13 +28,13 @@ /* /* qmgr_peer_create() creates an empty peer structure for the named /* job and destination. It is an error to call this function -/* if an peer for given combination already exists. +/* if a peer for given combination already exists. /* /* qmgr_peer_find() looks up the peer for the named destination -/* for the named job. A null result means that the queue +/* for the named job. A null result means that the peer /* was not found. /* -/* qmgr_peer_free() disposes of a per-job queue after all +/* qmgr_peer_free() disposes of a per-job peer after all /* its entries have been taken care of. It is an error to dispose /* of a peer still in use. /* @@ -42,7 +42,7 @@ /* has messages pending delivery. This routine implements /* round-robin search among job's peers. /* DIAGNOSTICS -/* Panic: consistency check failure. +/* None /* LICENSE /* .ad /* .fi @@ -68,7 +68,7 @@ /* qmgr_peer_create - create and initialize message peer structure */ -QMGR_PEER *qmgr_peer_create(QMGR_JOB *job, QMGR_QUEUE *queue) +QMGR_PEER *qmgr_peer_create(QMGR_JOB * job, QMGR_QUEUE *queue) { QMGR_PEER *peer; @@ -84,41 +84,41 @@ QMGR_PEER *qmgr_peer_create(QMGR_JOB *job, QMGR_QUEUE *queue) /* qmgr_peer_free - release peer structure */ -void qmgr_peer_free(QMGR_PEER *peer) +void qmgr_peer_free(QMGR_PEER * peer) { QMGR_JOB *job = peer->job; QMGR_LIST_UNLINK(job->peer_list, QMGR_PEER *, peer, peers); - htable_delete(job->peer_byname, peer->queue->name, (void (*) (char *)) 0) ; + htable_delete(job->peer_byname, peer->queue->name, (void (*) (char *)) 0); myfree((char *) peer); } /* qmgr_peer_find - lookup peer associated with given job and queue */ -QMGR_PEER *qmgr_peer_find(QMGR_JOB *job, QMGR_QUEUE *queue) +QMGR_PEER *qmgr_peer_find(QMGR_JOB * job, QMGR_QUEUE *queue) { return ((QMGR_PEER *) htable_find(job->peer_byname, queue->name)); } /* qmgr_peer_select - select next peer suitable for delivery within given job */ -QMGR_PEER *qmgr_peer_select(QMGR_JOB *job) +QMGR_PEER *qmgr_peer_select(QMGR_JOB * job) { QMGR_PEER *peer; QMGR_QUEUE *queue; - + /* * If we find a suitable site, rotate the list to enforce round-robin * selection. See similar selection code in qmgr_transport_select(). */ for (peer = job->peer_list.next; peer; peer = peer->peers.next) { - queue = peer->queue; - if (queue->window > queue->busy_refcount && peer->entry_list.next != 0) { - QMGR_LIST_ROTATE(job->peer_list, peer, peers); - if (msg_verbose) - msg_info("qmgr_peer_select: %s %s", job->message->queue_id, queue->name); + queue = peer->queue; + if (queue->window > queue->busy_refcount && peer->entry_list.next != 0) { + QMGR_LIST_ROTATE(job->peer_list, peer, peers); + if (msg_verbose) + msg_info("qmgr_peer_select: %s %s", job->message->queue_id, queue->name); return (peer); - } + } } return (0); } diff --git a/postfix/nqmgr/qmgr_transport.c b/postfix/nqmgr/qmgr_transport.c index 9fc1aabf5..e87ee7c44 100644 --- a/postfix/nqmgr/qmgr_transport.c +++ b/postfix/nqmgr/qmgr_transport.c @@ -320,10 +320,10 @@ QMGR_TRANSPORT *qmgr_transport_create(const char *name) */ transport->dest_concurrency_limit = get_mail_conf_int2(name, _DEST_CON_LIMIT, - var_dest_con_limit, 0, 0); + var_dest_con_limit, 0, 0); transport->recipient_limit = get_mail_conf_int2(name, _DEST_RCPT_LIMIT, - var_dest_rcpt_limit, 0, 0); + var_dest_rcpt_limit, 0, 0); if (transport->dest_concurrency_limit == 0 || transport->dest_concurrency_limit >= var_init_dest_concurrency) @@ -332,18 +332,18 @@ QMGR_TRANSPORT *qmgr_transport_create(const char *name) transport->init_dest_concurrency = transport->dest_concurrency_limit; transport->slot_cost = get_mail_conf_int2(name, _DELIVERY_SLOT_COST, - var_delivery_slot_cost, 0, 0); + var_delivery_slot_cost, 0, 0); transport->slot_loan = get_mail_conf_int2(name, _DELIVERY_SLOT_LOAN, - var_delivery_slot_loan, 0, 0); + var_delivery_slot_loan, 0, 0); transport->slot_loan_factor = - 100 - get_mail_conf_int2(name, _DELIVERY_SLOT_DISCOUNT, - var_delivery_slot_discount, 0, 100); + 100 - get_mail_conf_int2(name, _DELIVERY_SLOT_DISCOUNT, + var_delivery_slot_discount, 0, 100); transport->min_slots = get_mail_conf_int2(name, _MIN_DELIVERY_SLOTS, - var_min_delivery_slots, 0, 0); + var_min_delivery_slots, 0, 0); transport->rcpt_unused = get_mail_conf_int2(name, _XPORT_RCPT_LIMIT, - var_xport_rcpt_limit, 0, 0); + var_xport_rcpt_limit, 0, 0); transport->rcpt_per_stack = get_mail_conf_int2(name, _STACK_RCPT_LIMIT, - var_stack_rcpt_limit, 0, 0); + var_stack_rcpt_limit, 0, 0); transport->queue_byname = htable_create(0); QMGR_LIST_INIT(transport->queue_list); diff --git a/postfix/postalias/postalias.c b/postfix/postalias/postalias.c index dedf87c8e..3ff298489 100644 --- a/postfix/postalias/postalias.c +++ b/postfix/postalias/postalias.c @@ -5,7 +5,7 @@ /* Postfix alias database maintenance /* SYNOPSIS /* .fi -/* \fBpostalias\fR [\fB-Ninvw\fR] [\fB-c \fIconfig_dir\fR] +/* \fBpostalias\fR [\fB-Ninrvw\fR] [\fB-c \fIconfig_dir\fR] /* [\fB-d \fIkey\fR] [\fB-q \fIkey\fR] /* [\fIfile_type\fR:]\fIfile_name\fR ... /* DESCRIPTION @@ -42,6 +42,9 @@ /* Search the specified maps for \fIkey\fR and print the first value /* found on the standard output stream. The exit status is non-zero /* if the requested information was not found. +/* .IP \fB-r\fR +/* When updating a table, do not warn about duplicate entries; silently +/* replace them. /* .IP \fB-v\fR /* Enable verbose logging for debugging purposes. Multiple \fB-v\fR /* options make the software increasingly verbose. @@ -332,7 +335,7 @@ static int postalias_delete(const char *map_type, const char *map_name, static NORETURN usage(char *myname) { - msg_fatal("usage: %s [-Ninvw] [-c config_dir] [-d key] [-q key] [map_type:]file...", + msg_fatal("usage: %s [-Ninrvw] [-c config_dir] [-d key] [-q key] [map_type:]file...", myname); } @@ -382,7 +385,7 @@ int main(int argc, char **argv) /* * Parse JCL. */ - while ((ch = GETOPT(argc, argv, "Nc:d:inq:vw")) > 0) { + while ((ch = GETOPT(argc, argv, "Nc:d:inq:rvw")) > 0) { switch (ch) { default: usage(argv[0]); @@ -412,11 +415,15 @@ int main(int argc, char **argv) msg_fatal("specify only one of -q or -d"); query = optarg; break; + case 'r': + dict_flags &= ~(DICT_FLAG_DUP_WARN | DICT_FLAG_DUP_IGNORE); + dict_flags |= DICT_FLAG_DUP_REPLACE; + break; case 'v': msg_verbose++; break; case 'w': - dict_flags &= ~DICT_FLAG_DUP_WARN; + dict_flags &= ~(DICT_FLAG_DUP_WARN | DICT_FLAG_DUP_REPLACE); dict_flags |= DICT_FLAG_DUP_IGNORE; break; } diff --git a/postfix/postmap/postmap.c b/postfix/postmap/postmap.c index 3743bd971..da51ceb06 100644 --- a/postfix/postmap/postmap.c +++ b/postfix/postmap/postmap.c @@ -5,7 +5,7 @@ /* Postfix lookup table management /* SYNOPSIS /* .fi -/* \fBpostmap\fR [\fB-Ninvw\fR] [\fB-c \fIconfig_dir\fR] [\fB-d \fIkey\fR] +/* \fBpostmap\fR [\fB-Ninrvw\fR] [\fB-c \fIconfig_dir\fR] [\fB-d \fIkey\fR] /* [\fB-q \fIkey\fR] [\fIfile_type\fR:]\fIfile_name\fR ... /* DESCRIPTION /* The \fBpostmap\fR command creates or queries one or more Postfix @@ -60,6 +60,9 @@ /* Search the specified maps for \fIkey\fR and print the first value /* found on the standard output stream. The exit status is non-zero /* if the requested information was not found. +/* .IP \fB-r\fR +/* When updating a table, do not warn about duplicate entries; silently +/* replace them. /* .IP \fB-v\fR /* Enable verbose logging for debugging purposes. Multiple \fB-v\fR /* options make the software increasingly verbose. @@ -285,7 +288,7 @@ static int postmap_delete(const char *map_type, const char *map_name, static NORETURN usage(char *myname) { - msg_fatal("usage: %s [-Ninvw] [-c config_dir] [-d key] [-q key] [map_type:]file...", + msg_fatal("usage: %s [-Ninrvw] [-c config_dir] [-d key] [-q key] [map_type:]file...", myname); } @@ -335,7 +338,7 @@ int main(int argc, char **argv) /* * Parse JCL. */ - while ((ch = GETOPT(argc, argv, "Nc:d:inq:vw")) > 0) { + while ((ch = GETOPT(argc, argv, "Nc:d:inq:rvw")) > 0) { switch (ch) { default: usage(argv[0]); @@ -365,11 +368,15 @@ int main(int argc, char **argv) msg_fatal("specify only one of -q or -d"); query = optarg; break; + case 'r': + dict_flags &= ~(DICT_FLAG_DUP_WARN | DICT_FLAG_DUP_IGNORE); + dict_flags |= DICT_FLAG_DUP_REPLACE; + break; case 'v': msg_verbose++; break; case 'w': - dict_flags &= ~DICT_FLAG_DUP_WARN; + dict_flags &= ~(DICT_FLAG_DUP_WARN | DICT_FLAG_DUP_REPLACE); dict_flags |= DICT_FLAG_DUP_IGNORE; break; } diff --git a/postfix/proto/transport b/postfix/proto/transport index e7add092c..0905cd583 100644 --- a/postfix/proto/transport +++ b/postfix/proto/transport @@ -105,7 +105,7 @@ # .ti +5 # \fB\&.foo.org error:mail for *.foo.org is not deliverable\fR # -# This causes all mail for \fIuser\fR@\fIanything\fBfoo.org\fR +# This causes all mail for \fIuser\fR@\fIanything\fB.foo.org\fR # to be bounced. # REGULAR EXPRESSION TABLES # .ad diff --git a/postfix/qmgr/qmgr.c b/postfix/qmgr/qmgr.c index 7508a8d87..374008e92 100644 --- a/postfix/qmgr/qmgr.c +++ b/postfix/qmgr/qmgr.c @@ -477,7 +477,7 @@ int main(int argc, char **argv) VAR_QUEUE_RUN_DELAY, DEF_QUEUE_RUN_DELAY, &var_queue_run_delay, 1, 0, VAR_MIN_BACKOFF_TIME, DEF_MIN_BACKOFF_TIME, &var_min_backoff_time, 1, 0, VAR_MAX_BACKOFF_TIME, DEF_MAX_BACKOFF_TIME, &var_max_backoff_time, 1, 0, - VAR_MAX_QUEUE_TIME, DEF_MAX_QUEUE_TIME, &var_max_queue_time, 1, 0, + VAR_MAX_QUEUE_TIME, DEF_MAX_QUEUE_TIME, &var_max_queue_time, 1, 1000, VAR_QMGR_ACT_LIMIT, DEF_QMGR_ACT_LIMIT, &var_qmgr_active_limit, 1, 0, VAR_QMGR_RCPT_LIMIT, DEF_QMGR_RCPT_LIMIT, &var_qmgr_rcpt_limit, 1, 0, VAR_INIT_DEST_CON, DEF_INIT_DEST_CON, &var_init_dest_concurrency, 1, 0, diff --git a/postfix/qmgr/qmgr_message.c b/postfix/qmgr/qmgr_message.c index 7008cc7f3..c2167ce19 100644 --- a/postfix/qmgr/qmgr_message.c +++ b/postfix/qmgr/qmgr_message.c @@ -199,6 +199,7 @@ static int qmgr_message_read(QMGR_MESSAGE *message) long save_offset = message->rcpt_offset; /* save a flag */ char *start; struct stat st; + int nrcpt; /* * Initialize. No early returns or we have a memory leak. @@ -244,7 +245,8 @@ static int qmgr_message_read(QMGR_MESSAGE *message) start = vstring_str(buf); if (rec_type == REC_TYPE_SIZE) { if (message->data_size == 0) - message->data_size = atol(start); + sscanf(start, "%ld %ld %d", + &message->data_size, &message->data_offset, &nrcpt); } else if (rec_type == REC_TYPE_TIME) { if (message->arrival_time == 0) message->arrival_time = atol(start); @@ -258,7 +260,8 @@ static int qmgr_message_read(QMGR_MESSAGE *message) if (message->sender == 0) { message->sender = mystrdup(start); opened(message->queue_id, message->sender, - message->data_size, "queue %s", message->queue_name); + message->data_size, nrcpt, + "queue %s", message->queue_name); } } else if (rec_type == REC_TYPE_RCPT) { #define FUDGE(x) ((x) * (var_qmgr_fudge / 100.0)) diff --git a/postfix/smtp/smtp_connect.c b/postfix/smtp/smtp_connect.c index 6d73cb4ba..f3b2ab7a0 100644 --- a/postfix/smtp/smtp_connect.c +++ b/postfix/smtp/smtp_connect.c @@ -387,6 +387,12 @@ SMTP_SESSION *smtp_connect(char *destination, VSTRING *why) } else { session = smtp_connect_domain(host, port, why); } + if (session == 0 + && smtp_errno == SMTP_FAIL + && strcmp(host, var_relayhost) == 0) { + msg_warn("relayhost configuration problem: %s", var_relayhost); + smtp_errno = SMTP_RETRY; + } myfree(dest_buf); return (session); } diff --git a/postfix/smtpd/smtpd.c b/postfix/smtpd/smtpd.c index 4d8701e4f..765719bf5 100644 --- a/postfix/smtpd/smtpd.c +++ b/postfix/smtpd/smtpd.c @@ -258,6 +258,7 @@ #include #include #include +#include /* Global library. */ @@ -716,6 +717,8 @@ static void mail_reset(SMTPD_STATE *state) static int rcpt_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv) { char *err; + int narg; + char *arg; /* * Sanity checks. @@ -725,7 +728,7 @@ static int rcpt_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv) smtpd_chat_reply(state, "503 Error: need MAIL command"); return (-1); } - if (argc != 3 + if (argc < 3 || strcasecmp(argv[1].strval, "to:") != 0) { state->error_mask |= MAIL_ERROR_PROTOCOL; smtpd_chat_reply(state, "501 Syntax: RCPT TO:
"); @@ -741,6 +744,14 @@ static int rcpt_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv) smtpd_chat_reply(state, "%s", err); return (-1); } + for (narg = 3; narg < argc; narg++) { + arg = argv[narg].strval; + if (1) { + state->error_mask |= MAIL_ERROR_PROTOCOL; + smtpd_chat_reply(state, "555 Unsupported option: %s", arg); + return (-1); + } + } if (var_smtpd_rcpt_limit && state->rcpt_count >= var_smtpd_rcpt_limit) { state->error_mask |= MAIL_ERROR_POLICY; smtpd_chat_reply(state, "452 Error: too many recipients"); @@ -1184,6 +1195,7 @@ static void smtpd_proto(SMTPD_STATE *state) smtpd_chat_reply(state, "421 Error: too many errors"); break; } + watchdog_pat(); smtpd_chat_query(state); if ((argc = smtpd_token(vstring_str(state->buffer), &argv)) == 0) { state->error_mask |= MAIL_ERROR_PROTOCOL; diff --git a/postfix/util/vstring.c b/postfix/util/vstring.c index 65aca4fe1..f1669243a 100644 --- a/postfix/util/vstring.c +++ b/postfix/util/vstring.c @@ -1,4 +1,4 @@ -/* +/*++ /* NAME /* vstring 3 /* SUMMARY @@ -105,11 +105,11 @@ /* The function takes a VSTRING pointer and a list of zero /* or more (name,value) pairs. The expected valye type of the /* value depends on the specified name. The name codes are: -/* .IP VSTRING_CTL_MAXLEN (int) +/* .IP "VSTRING_CTL_MAXLEN (int)" /* Specifies a hard upper limit on a string's length. When the /* length would be exceeded, the program simulates a memory /* allocation problem (i.e. it terminates through msg_fatal()). -/* .IP VSTRING_CTL_END (no value) +/* .IP "VSTRING_CTL_END (no value)" /* Specifies the end of the argument list. Forgetting to terminate /* the argument list may cause the program to crash. /* .PP diff --git a/postfix/util/watchdog.c b/postfix/util/watchdog.c index da2ac1063..011b2fcb6 100644 --- a/postfix/util/watchdog.c +++ b/postfix/util/watchdog.c @@ -19,6 +19,8 @@ /* /* void watchdog_destroy(watchdog) /* WATCHDOG *watchdog; +/* +/* void watchdog_pat() /* DESCRIPTION /* This module implements watchdog timers that are based on ugly /* UNIX alarm timers. The module is designed to survive systems @@ -39,6 +41,8 @@ /* watchdog_destroy() stops the watchdog timer, and resumes the /* watchdog timer instance that was suspended by watchdog_create(). /* +/* watchdog_pat() pats the watchdog, so it stays quiet. +/* /* Arguments: /* .IP timeout /* The watchdog time limit. When the watchdog timer runs, the @@ -213,6 +217,18 @@ void watchdog_stop(WATCHDOG *wp) msg_info("%s: %p", myname, (char *) wp); } +/* watchdog_pat - pat the dog so it stays quiet */ + +void watchdog_pat(void) +{ + char *myname = "watchdog_pat"; + + if (watchdog_curr) + watchdog_curr->trip_run = 0; + if (msg_verbose) + msg_info("%s: %p", myname, (char *) watchdog_curr); +} + #ifdef TEST #include @@ -224,8 +240,9 @@ main(int unused_argc, char **unused_argv) msg_verbose = 1; wp = watchdog_create(10, (WATCHDOG_FN) 0, (char *) 0); + watchdog_start(wp); do { - watchdog_start(wp); + watchdog_pat(); } while (VSTREAM_GETCHAR() != VSTREAM_EOF); watchdog_destroy(wp); } diff --git a/postfix/util/watchdog.h b/postfix/util/watchdog.h index 926233b69..ee01faf9f 100644 --- a/postfix/util/watchdog.h +++ b/postfix/util/watchdog.h @@ -20,6 +20,7 @@ extern WATCHDOG *watchdog_create(unsigned, WATCHDOG_FN, char *); extern void watchdog_start(WATCHDOG *); extern void watchdog_stop(WATCHDOG *); extern void watchdog_destroy(WATCHDOG *); +extern void watchdog_pat(void); /* LICENSE /* .ad