diff --git a/postfix/ETRN_README b/postfix/ETRN_README index 7c72c3c77..ec87fd504 100644 --- a/postfix/ETRN_README +++ b/postfix/ETRN_README @@ -4,6 +4,12 @@ Purpose of this document This document describes the purpose of the Postfix fast ETRN service, how the service works, and how it can be tested. +Other documents with information on this subject: + +- conf/sample-flush.cf, sample configuration file +- conf/main.cf, sample configuration file +- flush(8), flush service implementation + The Postfix fast ETRN service ============================= diff --git a/postfix/conf/sample-flush.cf b/postfix/conf/sample-flush.cf new file mode 100644 index 000000000..0d6bb8154 --- /dev/null +++ b/postfix/conf/sample-flush.cf @@ -0,0 +1,50 @@ +# DO NOT EDIT THIS FILE. EDIT THE MAIN.CF FILE INSTEAD. THE STUFF +# HERE JUST SERVES AS AN EXAMPLE. +# +# This file contains example settings of Postfix parameters that +# control the fast flush service, which is the engine that implements +# ETRN and "sendmail -qR". + +# The fast_flush_policy parameter specifies what destinations are +# eligible for per-destination logfiles with mail that is queued to +# those destinations. +# +# When a destination is eligible, ETRN and "sendmail -qR" are +# implemented by delivering only messages that are queued for that +# destination (Postfix will deliver to all recipients of those +# messages, regardless of their destination). +# +# When a destination is not eligible, ETRN and "sendmail -qR" are +# implemented simply by attempting to deliver all queued mail. +# +# By default, Postfix maintains per-destination deferred mail logfiles +# only for destinations that the Postfix SMTP server is willing to +# relay to (see the relay_domains parameter in sample-smtpd.cf). +# +# Specify "all" to enable per-destination deferred mail logfiles +# for all destinations, "none" to disable the logfiles altogether. +# +#fast_flush_policy = all +fast_flush_policy = relay +#fast_flush_policy = none + +# The fast_flush_purge_delay parameter controls how long an empty +# per-destination deferred mail logfile is allowed to live. +# +# You can specify the time as a number, or as a number followed by +# a letter that indicates the time unit: s=seconds, m=minutes, h=hours, +# d=days, w=weeks. The default time unit is days. +# +fast_flush_purge_delay = 7d + +# The fast_flush_refresh_delay parameter controls how long a non-empty +# per-destination deferred mail logfile is allowed to remain unread +# before its contents need to be refreshed. The contents of a logfile +# are refreshed by requesting delivery of messages listed in the +# logfile. +# +# You can specify the time as a number, or as a number followed by +# a letter that indicates the time unit: s=seconds, m=minutes, h=hours, +# d=days, w=weeks. The default time unit is hours. +# +fast_flush_refresh_delay = 12h diff --git a/postfix/html/flush.8.html b/postfix/html/flush.8.html index eb48f6b69..5f00b04ae 100644 --- a/postfix/html/flush.8.html +++ b/postfix/html/flush.8.html @@ -18,47 +18,47 @@ FLUSH(8) FLUSH(8) equivalent, sendmail -qR. This program expects to be run from the master(8) process manager. - The record is implemented as per-destination logfiles with - as contents the queue IDs of deferred mail. The files are - append-only, and are truncated when delivery is requested - for a specific site. + The record is implemented as a per-destination logfile + with as contents the queue IDs of deferred mail. A logfile + is append-only, and is truncated when delivery is + requested for the corresponding destination. A destination + is the part on the right-hand side of the right-most @ in + an email address. - Deferred mail by destination information is recorded only - for destinations that are eligible according to a config- - urable policy. The policy is specified with the - fast_flush_cache_policy configuration parameter: + Per-destination logfiles of deferred mail are maintained + only for eligible destinations. The policy is specified + with the fast_flush_cache_policy configuration parameter: - all Maintain per-destination deferred mail logfiles for - all destinations. + all Maintain per-destination logfiles for all destina- + tions. - relay Maintain per-destination deferred mail logfiles - only for destinations that this system is willing - to relay mail to ($relay_domains). + relay (default policy) + Maintain per-destination logfiles only for destina- + tions that this system is willing to relay mail to + (as controlled by the relay_domains configuration + parameter). - none Do not maintain per-destination deferred mail log- - files. + none Do not maintain per-destination logfiles. This server implements the following requests: FLUSH_REQ_ADD sitename queue_id Inform the cache manager that the specified message - is queued for the specified site. Depending on - caching policy, the cache manager stores or ignores - the information. + is queued for sitename. Depending on caching pol- + icy, the cache manager stores or ignores the infor- + mation. FLUSH_REQ_SEND sitename - Request delivery of all messages that are queued - for the specified site. Depending on cache policy, - this triggers delivery of specific messages or of - all queued mail. The per-destination logfile is - discarded. - - TRIGGER_REQ_WAKEUP (wakeup signal from master) - - FLUSH_REQ_PURGE - Delete empty per-destination logfiles that haven't - been updated in $fast_flush_purge_delay seconds. + Request delivery of mail that is queued for site- + name. If the destination is eligible for a fast + flush logfile, this request triggers delivery of + specific messages; the per-destination logfile is + truncated to zero length; if mail is undeliverable, + it will be logged to the per-destination logfile. + If the destination is not eligible for a fast flush + logfile, this request triggers delivery of all + queued mail. @@ -71,22 +71,36 @@ FLUSH(8) FLUSH(8) FLUSH(8) FLUSH(8) + TRIGGER_REQ_WAKEUP + This wakeup request from the master is an alterna- + tive way to request FLUSH_REQ_REFRESH. + + FLUSH_REQ_REFRESH (completes in the background) Refresh non-empty per-destination logfiles that - were not read in $fast_flush_refresh_delay seconds. - This is done by pretending that send requests were - received for the corresponding sites. + were not read in $fast_flush_refresh_delay hours, + by simulating send requests (see above) for the + corresponding destinations. - Fast flush logfiles are truncated only after a - FLUSH_REQ_SEND request, not when mail is actually - delivered, and therefore can accumulate outdated or - redundant data. In order to maintain sanity, - FLUSH_REQ_PURGE should be requested at regular - imtervals. + Delete empty per-destination logfiles that were not + updated in fast_flush_purge_delay days. - After an initial sanity check of request parame- - ters, this request proceeds in the background. + FLUSH_REQ_PURGE (completes in the background) + Refresh all non-empty per-destination logfiles, by + simulating send requests (see above) for the corre- + sponding destinations. This can be incredibly + expensive when caching is enabled for all deferred + mail, and is not recommended. - The response to the client is one of: + Delete empty per-destination logfiles that were not + updated in fast_flush_purge_delay days. + + Fast flush logfiles are truncated only after a + FLUSH_REQ_SEND request, not when mail is actually deliv- + ered, and therefore can accumulate outdated or redundant + data. In order to maintain sanity, FLUSH_REQ_REFRESH must + be executed periodically. + + The server response is one of: FLUSH_STAT_OK The request completed normally. @@ -109,8 +123,20 @@ FLUSH(8) FLUSH(8) BUGS In reality, this server schedules delivery of all recipi- - ents of deferred messages. This limitation is due to the + ents of a deferred message. This limitation is due to the fact that one queue runner has to handle mail for multiple + + + + 2 + + + + + +FLUSH(8) FLUSH(8) + + destinations. FILES @@ -125,26 +151,16 @@ FLUSH(8) FLUSH(8) What destinations can have a "fast flush" logfile: all, relay (relay destinations) or none. - - - - 2 - - - - - -FLUSH(8) FLUSH(8) - - fast_flush_refresh_delay Refresh a non-empty "fast flush" logfile that was - not read in this amount of time, by simulating a - send request for the corresponding destination. + not read in this amount of time (default time unit: + hours), by simulating a send request for the corre- + sponding destination. fast_flush_purge_delay - Remove an empty "fast flush" logfile that was not - updated in this amount of time. + Remove an empty "fast flush" logfile that was not + updated in this amount of time (default time unit: + days). SEE ALSO smtpd(8) Postfix SMTP server @@ -173,22 +189,6 @@ FLUSH(8) FLUSH(8) - - - - - - - - - - - - - - - - diff --git a/postfix/man/man8/flush.8 b/postfix/man/man8/flush.8 index 9deacb75f..ddf141ef5 100644 --- a/postfix/man/man8/flush.8 +++ b/postfix/man/man8/flush.8 @@ -19,51 +19,65 @@ This information is used to improve the performance of the SMTP This program expects to be run from the \fBmaster\fR(8) process manager. -The record is implemented as per-destination logfiles with -as contents the queue IDs of deferred mail. The files are -append-only, and are truncated when delivery is requested -for a specific site. +The record is implemented as a per-destination logfile with +as contents the queue IDs of deferred mail. A logfile is +append-only, and is truncated when delivery is requested +for the corresponding destination. A destination is the +part on the right-hand side of the right-most \fB@\fR in +an email address. -Deferred mail by destination information is recorded only for -destinations that are eligible according to a configurable policy. -The policy is specified with the \fBfast_flush_cache_policy\fR -configuration parameter: +Per-destination logfiles of deferred mail are maintained only for +eligible destinations. The policy is specified with the +\fBfast_flush_cache_policy\fR configuration parameter: .IP \fBall\fR -Maintain per-destination deferred mail logfiles for all destinations. -.IP \fBrelay\fR -Maintain per-destination deferred mail logfiles only for destinations -that this system is willing to relay mail to ($\fBrelay_domains\fR). +Maintain per-destination logfiles for all destinations. +.IP "\fBrelay\fR (default policy)" +Maintain per-destination logfiles only for destinations +that this system is willing to relay mail to (as controlled +by the \fBrelay_domains\fR configuration parameter). .IP \fBnone\fR -Do not maintain per-destination deferred mail logfiles. +Do not maintain per-destination logfiles. .PP This server implements the following requests: .IP "\fBFLUSH_REQ_ADD\fI sitename queue_id\fR" Inform the cache manager that the specified message is queued for -the specified site. Depending on caching policy, the cache manager +\fIsitename\fR. Depending on caching policy, the cache manager stores or ignores the information. .IP "\fBFLUSH_REQ_SEND\fI sitename\fR" -Request delivery of all messages that are queued for the specified -site. Depending on cache policy, this triggers delivery of specific -messages or of all queued mail. The per-destination logfile is -discarded. -.IP "\fBTRIGGER_REQ_WAKEUP\fR (wakeup signal from master)" -.IP "\fBFLUSH_REQ_PURGE\fR" -Delete empty per-destination logfiles that haven't been updated in -$\fBfast_flush_purge_delay\fR seconds. +Request delivery of mail that is queued for \fIsitename\fR. +If the destination is eligible for a fast flush logfile, +this request triggers delivery of specific messages; the +per-destination logfile is truncated to zero length; if mail +is undeliverable, it will be logged to the per-destination +logfile. .sp +If the destination is not eligible for a fast flush logfile, +this request triggers delivery of all queued mail. +.IP \fBTRIGGER_REQ_WAKEUP\fR +This wakeup request from the master is an alternative way to +request \fBFLUSH_REQ_REFRESH\fR. +.IP "\fBFLUSH_REQ_REFRESH\fR (completes in the background)" Refresh non-empty per-destination logfiles that were not read in -$\fBfast_flush_refresh_delay\fR seconds. This is done by pretending -that send requests were received for the corresponding sites. +$\fBfast_flush_refresh_delay\fR hours, by simulating +send requests (see above) for the corresponding destinations. .sp +Delete empty per-destination logfiles that were not updated in +\fBfast_flush_purge_delay\fR days. +.IP "\fBFLUSH_REQ_PURGE\fR (completes in the background)" +Refresh all non-empty per-destination logfiles, by simulating +send requests (see above) for the corresponding destinations. +This can be incredibly expensive when caching is enabled for +all deferred mail, and is not recommended. +.sp +Delete empty per-destination logfiles that were not updated in +\fBfast_flush_purge_delay\fR days. +.PP Fast flush logfiles are truncated only after a \fBFLUSH_REQ_SEND\fR request, not when mail is actually delivered, and therefore can accumulate outdated or redundant data. In order to maintain sanity, -\fBFLUSH_REQ_PURGE\fR should be requested at regular imtervals. -.sp -After an initial sanity check of request parameters, this request -proceeds in the background. -.PP -The response to the client is one of: +\fBFLUSH_REQ_REFRESH\fR must be executed periodically. + +The server response is one of: .IP \fBFLUSH_STAT_OK\fR The request completed normally. .IP \fBFLUSH_STAT_BAD\fR @@ -87,7 +101,7 @@ Problems and transactions are logged to \fBsyslogd\fR(8). .ad .fi In reality, this server schedules delivery of all recipients -of deferred messages. This limitation is due to the fact that +of a deferred message. This limitation is due to the fact that one queue runner has to handle mail for multiple destinations. .SH FILES .na @@ -106,11 +120,11 @@ What destinations can have a "fast flush" logfile: \fBall\fR, \fBrelay\fR (relay destinations) or \fBnone\fR. .IP \fBfast_flush_refresh_delay\fR Refresh a non-empty "fast flush" logfile that was not read in -this amount of time, by simulating a send request for the -corresponding destination. +this amount of time (default time unit: hours), by simulating +a send request for the corresponding destination. .IP \fBfast_flush_purge_delay\fR Remove an empty "fast flush" logfile that was not updated in -this amount of time. +this amount of time (default time unit: days). .SH SEE ALSO .na .nf diff --git a/postfix/src/flush/flush.c b/postfix/src/flush/flush.c index 98c1f8ed5..c1157b0ad 100644 --- a/postfix/src/flush/flush.c +++ b/postfix/src/flush/flush.c @@ -13,51 +13,65 @@ /* This program expects to be run from the \fBmaster\fR(8) process /* manager. /* -/* The record is implemented as per-destination logfiles with -/* as contents the queue IDs of deferred mail. The files are -/* append-only, and are truncated when delivery is requested -/* for a specific site. +/* The record is implemented as a per-destination logfile with +/* as contents the queue IDs of deferred mail. A logfile is +/* append-only, and is truncated when delivery is requested +/* for the corresponding destination. A destination is the +/* part on the right-hand side of the right-most \fB@\fR in +/* an email address. /* -/* Deferred mail by destination information is recorded only for -/* destinations that are eligible according to a configurable policy. -/* The policy is specified with the \fBfast_flush_cache_policy\fR -/* configuration parameter: +/* Per-destination logfiles of deferred mail are maintained only for +/* eligible destinations. The policy is specified with the +/* \fBfast_flush_cache_policy\fR configuration parameter: /* .IP \fBall\fR -/* Maintain per-destination deferred mail logfiles for all destinations. -/* .IP \fBrelay\fR -/* Maintain per-destination deferred mail logfiles only for destinations -/* that this system is willing to relay mail to ($\fBrelay_domains\fR). +/* Maintain per-destination logfiles for all destinations. +/* .IP "\fBrelay\fR (default policy)" +/* Maintain per-destination logfiles only for destinations +/* that this system is willing to relay mail to (as controlled +/* by the \fBrelay_domains\fR configuration parameter). /* .IP \fBnone\fR -/* Do not maintain per-destination deferred mail logfiles. +/* Do not maintain per-destination logfiles. /* .PP /* This server implements the following requests: /* .IP "\fBFLUSH_REQ_ADD\fI sitename queue_id\fR" /* Inform the cache manager that the specified message is queued for -/* the specified site. Depending on caching policy, the cache manager +/* \fIsitename\fR. Depending on caching policy, the cache manager /* stores or ignores the information. /* .IP "\fBFLUSH_REQ_SEND\fI sitename\fR" -/* Request delivery of all messages that are queued for the specified -/* site. Depending on cache policy, this triggers delivery of specific -/* messages or of all queued mail. The per-destination logfile is -/* discarded. -/* .IP "\fBTRIGGER_REQ_WAKEUP\fR (wakeup signal from master)" -/* .IP "\fBFLUSH_REQ_PURGE\fR" -/* Delete empty per-destination logfiles that haven't been updated in -/* $\fBfast_flush_purge_delay\fR seconds. +/* Request delivery of mail that is queued for \fIsitename\fR. +/* If the destination is eligible for a fast flush logfile, +/* this request triggers delivery of specific messages; the +/* per-destination logfile is truncated to zero length; if mail +/* is undeliverable, it will be logged to the per-destination +/* logfile. /* .sp +/* If the destination is not eligible for a fast flush logfile, +/* this request triggers delivery of all queued mail. +/* .IP \fBTRIGGER_REQ_WAKEUP\fR +/* This wakeup request from the master is an alternative way to +/* request \fBFLUSH_REQ_REFRESH\fR. +/* .IP "\fBFLUSH_REQ_REFRESH\fR (completes in the background)" /* Refresh non-empty per-destination logfiles that were not read in -/* $\fBfast_flush_refresh_delay\fR seconds. This is done by pretending -/* that send requests were received for the corresponding sites. +/* $\fBfast_flush_refresh_delay\fR hours, by simulating +/* send requests (see above) for the corresponding destinations. /* .sp +/* Delete empty per-destination logfiles that were not updated in +/* \fBfast_flush_purge_delay\fR days. +/* .IP "\fBFLUSH_REQ_PURGE\fR (completes in the background)" +/* Refresh all non-empty per-destination logfiles, by simulating +/* send requests (see above) for the corresponding destinations. +/* This can be incredibly expensive when caching is enabled for +/* all deferred mail, and is not recommended. +/* .sp +/* Delete empty per-destination logfiles that were not updated in +/* \fBfast_flush_purge_delay\fR days. +/* .PP /* Fast flush logfiles are truncated only after a \fBFLUSH_REQ_SEND\fR /* request, not when mail is actually delivered, and therefore can /* accumulate outdated or redundant data. In order to maintain sanity, -/* \fBFLUSH_REQ_PURGE\fR should be requested at regular imtervals. -/* .sp -/* After an initial sanity check of request parameters, this request -/* proceeds in the background. -/* .PP -/* The response to the client is one of: +/* \fBFLUSH_REQ_REFRESH\fR must be executed periodically. +/* +/* The server response is one of: /* .IP \fBFLUSH_STAT_OK\fR /* The request completed normally. /* .IP \fBFLUSH_STAT_BAD\fR @@ -75,7 +89,7 @@ /* Problems and transactions are logged to \fBsyslogd\fR(8). /* BUGS /* In reality, this server schedules delivery of all recipients -/* of deferred messages. This limitation is due to the fact that +/* of a deferred message. This limitation is due to the fact that /* one queue runner has to handle mail for multiple destinations. /* FILES /* /var/spool/postfix/flush, location of "fast flush" logfiles. @@ -90,11 +104,11 @@ /* \fBrelay\fR (relay destinations) or \fBnone\fR. /* .IP \fBfast_flush_refresh_delay\fR /* Refresh a non-empty "fast flush" logfile that was not read in -/* this amount of time, by simulating a send request for the -/* corresponding destination. +/* this amount of time (default time unit: hours), by simulating +/* a send request for the corresponding destination. /* .IP \fBfast_flush_purge_delay\fR /* Remove an empty "fast flush" logfile that was not updated in -/* this amount of time. +/* this amount of time (default time unit: days). /* SEE ALSO /* smtpd(8) Postfix SMTP server /* qmgr(8) Postfix queue manager @@ -316,11 +330,20 @@ static int flush_send_service(const char *site) /* * This is the part that dominates running time: schedule the listed - * queue files for delivery by updating their file time stamps. This - * should take no more than a couple seconds under normal conditions. - * Filter out duplicate names to avoid hammering the file system, with + * queue files for delivery by updating their file time stamps and by + * moving them from the deferred queue to the incoming queue. This should + * take no more than a couple seconds under normal conditions. Filter out + * duplicate queue file names to avoid hammering the file system, with * some finite limit on the amount of memory that we are willing to - * sacrifice. Graceful degradation. + * sacrifice for duplicate filtering. Graceful degradation. + * + * By moving selected queue files from the deferred queue to the incoming + * queue we optimize for the case where most deferred mail is for other + * sites. If that assumption does not hold, i.e. all deferred mail is for + * the same site, then doing a "fast flush" will cost more disk I/O than + * a "slow flush" that delivers the entire deferred queue. This penalty + * is only temporary - it will go away after we unite the active queue + * and the incoming queue. */ queue_id = vstring_alloc(10); queue_file = vstring_alloc(10); @@ -385,11 +408,11 @@ static int flush_send_service(const char *site) return (FLUSH_STAT_OK); } -/* flush_purge_service - housekeeping */ +/* flush_refresh_service - refresh logfiles beyond some age */ -static int flush_purge_service(void) +static int flush_refresh_service(int max_age) { - char *myname = "flush_purge_service"; + char *myname = "flush_refresh_service"; SCAN_DIR *scan; char *site; struct stat st; @@ -397,11 +420,15 @@ static int flush_purge_service(void) scan = scan_dir_open(MAIL_QUEUE_FLUSH); while ((site = mail_scan_dir_next(scan)) != 0) { + if (!mail_queue_id_ok(site)) + continue; /* XXX grumble. */ mail_queue_path(path, MAIL_QUEUE_FLUSH, site); - if (valid_hostname(site) == 0) { - msg_warn("%s: bad fast flush logfile name: %s", myname, site); + if (flush_policy_ok(site) == 0) { if (unlink(STR(path)) < 0) msg_warn("remove %s: %m", STR(path)); + else if (msg_verbose) + msg_info("%s: spurious fast flush logfile name: %s", + myname, site); continue; } if (stat(STR(path), &st) < 0) { @@ -420,14 +447,14 @@ static int flush_purge_service(void) myname, STR(path), var_fflush_purge / 86400); } else if (msg_verbose) msg_info("%s: skip site %s - empty log", myname, site); - } else if (st.st_atime + var_fflush_refresh < event_time()) { + } else if (st.st_atime + max_age < event_time()) { if (msg_verbose) msg_info("%s: flush site %s", myname, site); flush_send_service(site); } else { if (msg_verbose) msg_info("%s: skip site %s, unread for <%d hours(s) ", - myname, site, var_fflush_refresh / 3600); + myname, site, max_age / 3600); } } scan_dir_close(scan); @@ -448,7 +475,7 @@ static void flush_service(VSTREAM *client_stream, char *unused_service, TRIGGER_REQ_WAKEUP, 0, }; - int status = FLUSH_STAT_OK; + int status = FLUSH_STAT_BAD; /* * Sanity check. This service takes no command-line arguments. @@ -471,23 +498,29 @@ static void flush_service(VSTREAM *client_stream, char *unused_service, if (STREQ(STR(request), FLUSH_REQ_ADD)) { site = vstring_alloc(10); queue_id = vstring_alloc(10); - if (mail_scan(client_stream, "%s %s", site, queue_id) == 2 + if (mail_command_read(client_stream, "%s %s", site, queue_id) == 2 && valid_hostname(STR(site)) - && mail_queue_id_ok(STR(queue_id))) { + && mail_queue_id_ok(STR(queue_id))) status = flush_add_service(STR(site), STR(queue_id)); - } + mail_print(client_stream, "%d", status); } else if (STREQ(STR(request), FLUSH_REQ_SEND)) { site = vstring_alloc(10); - if (mail_scan(client_stream, "%s", site) == 1 - && valid_hostname(STR(site))) { + if (mail_command_read(client_stream, "%s", site) == 1 + && valid_hostname(STR(site))) status = flush_send_service(STR(site)); - } - } else if (STREQ(STR(request), FLUSH_REQ_PURGE) + mail_print(client_stream, "%d", status); + } else if (STREQ(STR(request), FLUSH_REQ_REFRESH) || STREQ(STR(request), wakeup)) { - status = flush_purge_service(); + mail_print(client_stream, "%d", FLUSH_STAT_OK); + vstream_fflush(client_stream); + (void) flush_refresh_service(var_fflush_refresh); + } else if (STREQ(STR(request), FLUSH_REQ_PURGE)) { + mail_print(client_stream, "%d", FLUSH_STAT_OK); + vstream_fflush(client_stream); + (void) flush_refresh_service(0); } - } - mail_print(client_stream, "%d", status); + } else + mail_print(client_stream, "%d", status); vstring_free(request); if (site) vstring_free(site); @@ -500,8 +533,8 @@ static void flush_service(VSTREAM *client_stream, char *unused_service, int main(int argc, char **argv) { static CONFIG_TIME_TABLE time_table[] = { - VAR_FFLUSH_REFRESH, DEF_FFLUSH_REFRESH, &var_fflush_refresh, 1, 0, - VAR_FFLUSH_PURGE, DEF_FFLUSH_PURGE, &var_fflush_purge, 1, 0, + VAR_FFLUSH_REFRESH, DEF_FFLUSH_REFRESH, &var_fflush_refresh, 'h', 1, 0, + VAR_FFLUSH_PURGE, DEF_FFLUSH_PURGE, &var_fflush_purge, 'd', 1, 0, 0, }; static CONFIG_STR_TABLE str_table[] = { diff --git a/postfix/src/global/defer.c b/postfix/src/global/defer.c index 83e8776b0..59cde38fa 100644 --- a/postfix/src/global/defer.c +++ b/postfix/src/global/defer.c @@ -158,7 +158,8 @@ int vdefer_append(int flags, const char *id, const char *recipient, * bounce/defer daemon? Well, doing it here is more robust. */ if ((rcpt_domain = strrchr(recipient, '@')) != 0 && *++rcpt_domain != 0) - flush_add(rcpt_domain, id); + if (flush_add(rcpt_domain, id) != FLUSH_STAT_OK) + msg_warn("unable to talk to fast flush service"); return (-1); } diff --git a/postfix/src/global/flush_clnt.c b/postfix/src/global/flush_clnt.c index 3e28b8825..26b2899f5 100644 --- a/postfix/src/global/flush_clnt.c +++ b/postfix/src/global/flush_clnt.c @@ -13,6 +13,8 @@ /* int flush_send(site) /* const char *site; /* +/* int flush_refresh() +/* /* int flush_purge() /* DESCRIPTION /* The following routines operate through the "fast flush" service. @@ -26,13 +28,18 @@ /* flush_send() requests delivery of all mail that is queued for /* the specified destination. /* +/* flush_refresh() requests the "fast flush" cache manager to refresh +/* cached information that was not used for some configurable amount +/* time. +/* /* flush_purge() requests the "fast flush" cache manager to refresh -/* cached information that was not used or not updated for some -/* configurable amount of time. +/* all cached information. This is incredibly expensive, and is not +/* recommended. /* DIAGNOSTICS /* The result codes and their meanings are (see flush_clnt(5h)): /* .IP MAIL_FLUSH_OK -/* The request completed successfully. +/* The request completed successfully (in case of requests that +/* complete in the background: the request was accepted by the server). /* .IP MAIL_FLUSH_FAIL /* The request failed (the request could not be sent to the server, /* or the server reported failure). @@ -74,46 +81,6 @@ #define STR(x) vstring_str(x) -/* flush_clnt - generic fast flush service client */ - -static int flush_clnt(const char *format,...) -{ - VSTREAM *flush; - int status; - va_list ap; - - /* - * Connect to the fast flush service over local IPC. - */ - if ((flush = mail_connect(MAIL_CLASS_PRIVATE, MAIL_SERVICE_FLUSH, - BLOCKING)) == 0) - return (FLUSH_STAT_FAIL); - - /* - * Do not get stuck forever. - */ - vstream_control(flush, - VSTREAM_CTL_TIMEOUT, var_ipc_timeout, - VSTREAM_CTL_END); - - /* - * Send a request with the site name, and receive the request acceptance - * status. - */ - va_start(ap, format); - mail_vprint(flush, format, ap); - va_end(ap); - if (mail_scan(flush, "%d", &status) != 1) - status = FLUSH_STAT_FAIL; - - /* - * Clean up. - */ - vstream_fclose(flush); - - return (status); -} - /* flush_purge - house keeping */ int flush_purge(void) @@ -130,7 +97,33 @@ int flush_purge(void) if (strcmp(var_fflush_policy, FFLUSH_POLICY_NONE) == 0) status = FLUSH_STAT_OK; else - status = flush_clnt("%s", FLUSH_REQ_PURGE); + status = mail_command_write(MAIL_CLASS_PRIVATE, MAIL_SERVICE_FLUSH, + "%s", FLUSH_REQ_PURGE); + + if (msg_verbose) + msg_info("%s: status %d", myname, status); + + return (status); +} + +/* flush_refresh - house keeping */ + +int flush_refresh(void) +{ + char *myname = "flush_refresh"; + int status; + + if (msg_verbose) + msg_info("%s", myname); + + /* + * Don't bother the server if the service is turned off. + */ + if (strcmp(var_fflush_policy, FFLUSH_POLICY_NONE) == 0) + status = FLUSH_STAT_OK; + else + status = mail_command_write(MAIL_CLASS_PRIVATE, MAIL_SERVICE_FLUSH, + "%s", FLUSH_REQ_REFRESH); if (msg_verbose) msg_info("%s: status %d", myname, status); @@ -154,7 +147,8 @@ int flush_send(const char *site) if (strcmp(var_fflush_policy, FFLUSH_POLICY_NONE) == 0) status = mail_flush_deferred(); else - status = flush_clnt("%s %s", FLUSH_REQ_SEND, site); + status = mail_command_write(MAIL_CLASS_PRIVATE, MAIL_SERVICE_FLUSH, + "%s %s", FLUSH_REQ_SEND, site); if (msg_verbose) msg_info("%s: site %s status %d", myname, site, status); @@ -178,7 +172,8 @@ int flush_add(const char *site, const char *queue_id) if (strcmp(var_fflush_policy, FFLUSH_POLICY_NONE) == 0) status = FLUSH_STAT_OK; else - status = flush_clnt("%s %s %s", FLUSH_REQ_ADD, site, queue_id); + status = mail_command_write(MAIL_CLASS_PRIVATE, MAIL_SERVICE_FLUSH, + "%s %s %s", FLUSH_REQ_ADD, site, queue_id); if (msg_verbose) msg_info("%s: site %s id %s status %d", myname, site, queue_id, diff --git a/postfix/src/global/flush_clnt.h b/postfix/src/global/flush_clnt.h index 7838e7a14..47a88558a 100644 --- a/postfix/src/global/flush_clnt.h +++ b/postfix/src/global/flush_clnt.h @@ -16,6 +16,7 @@ */ extern int flush_add(const char *, const char *); extern int flush_send(const char *); +extern int flush_refresh(void); extern int flush_purge(void); /* @@ -23,7 +24,8 @@ extern int flush_purge(void); */ #define FLUSH_REQ_ADD "add" /* append queue ID to site log */ #define FLUSH_REQ_SEND "send" /* flush mail queued for site */ -#define FLUSH_REQ_PURGE "purge" /* refresh or delete old info */ +#define FLUSH_REQ_REFRESH "rfrsh" /* refresh old logfiles */ +#define FLUSH_REQ_PURGE "purge" /* refresh all logfiles */ /* * Mail flush server status codes. diff --git a/postfix/src/global/mail_command_write.c b/postfix/src/global/mail_command_write.c index 721a96464..e6e855788 100644 --- a/postfix/src/global/mail_command_write.c +++ b/postfix/src/global/mail_command_write.c @@ -23,7 +23,8 @@ /* .IP format /* Format string understood by mail_print(3). /* DIAGNOSTICS -/* The result is zero in case of success, non-zero otherwise. +/* The result is -1 if the request could not be sent, otherwise +/* the result is the status reported by the server. /* Warnings: problems connecting to the requested service. /* Fatal: out of memory. /* SEE ALSO @@ -43,7 +44,7 @@ /* System library. */ #include -#include /* 44BSD stdarg.h uses abort() */ +#include /* 44BSD stdarg.h uses abort() */ #include /* Utility library. */ @@ -67,13 +68,14 @@ int mail_command_write(const char *class, const char *name, * Talk a little protocol with the specified service. */ if ((stream = mail_connect(class, name, BLOCKING)) == 0) - return (1); + return (-1); va_start(ap, fmt); status = mail_vprint(stream, fmt, ap); va_end(ap); - status |= mail_print(stream, "%s", MAIL_EOF); - status |= vstream_fflush(stream); - if (mail_scan(stream, "%d", &status) != 1) + if (status != 0 + || mail_print(stream, "%s", MAIL_EOF) != 0 + || vstream_fflush(stream) != 0 + || mail_scan(stream, "%d", &status) != 1) status = -1; (void) vstream_fclose(stream); return (status); diff --git a/postfix/src/global/mail_conf.h b/postfix/src/global/mail_conf.h index 55dbf5836..60bd859e2 100644 --- a/postfix/src/global/mail_conf.h +++ b/postfix/src/global/mail_conf.h @@ -46,11 +46,11 @@ extern const char *mail_conf_lookup_eval(const char *); extern char *get_mail_conf_str(const char *, const char *, int, int); extern int get_mail_conf_int(const char *, int, int, int); extern int get_mail_conf_bool(const char *, int); -extern int get_mail_conf_time(const char *, const char *, int, int); +extern int get_mail_conf_time(const char *, const char *, int, int, int); extern char *get_mail_conf_raw(const char *, const char *, int, int); extern int get_mail_conf_int2(const char *, const char *, int, int, int); -extern int get_mail_conf_time2(const char *, const char *, const char *, int, int); +extern int get_mail_conf_time2(const char *, const char *, const char *, int, int, int); /* * Lookup with function-call defaults. @@ -58,7 +58,7 @@ extern int get_mail_conf_time2(const char *, const char *, const char *, int, in extern char *get_mail_conf_str_fn(const char *, const char *(*) (void), int, int); extern int get_mail_conf_int_fn(const char *, int (*) (void), int, int); extern int get_mail_conf_bool_fn(const char *, int (*) (void)); -extern int get_mail_conf_time_fn(const char *, const char *(*) (void), int, int); +extern int get_mail_conf_time_fn(const char *, const char *(*) (void), int, int, int); extern char *get_mail_conf_raw_fn(const char *, const char *(*) (void), int, int); /* @@ -99,6 +99,7 @@ typedef struct { const char *name; /* config variable name */ const char *defval; /* default value */ int *target; /* pointer to global variable */ + int def_unit; /* default unit: s|m|h|d|w */ int min; /* lower bound or zero */ int max; /* upper bound or zero */ } CONFIG_TIME_TABLE; @@ -139,6 +140,7 @@ typedef struct { const char *name; /* config variable name */ const char *(*defval) (void); /* default value provider */ int *target; /* pointer to global variable */ + int def_unit; /* default unit: s|m|h|d|w */ int min; /* lower bound or zero */ int max; /* upper bound or zero */ } CONFIG_TIME_FN_TABLE; diff --git a/postfix/src/global/mail_conf_time.c b/postfix/src/global/mail_conf_time.c index 7a6c1ec28..dafd836cf 100644 --- a/postfix/src/global/mail_conf_time.c +++ b/postfix/src/global/mail_conf_time.c @@ -6,15 +6,17 @@ /* SYNOPSIS /* #include /* -/* int get_mail_conf_time(name, defval, min, max); +/* int get_mail_conf_time(name, defval, def_unit, min, max); /* const char *name; /* const char *defval; +/* int def_unit; /* int min; /* int max; /* -/* int get_mail_conf_time_fn(name, defval, min, max); +/* int get_mail_conf_time_fn(name, defval, def_unit, min, max); /* const char *name; /* const char *(*defval)(); +/* int def_unit; /* int min; /* int max; /* @@ -28,22 +30,23 @@ /* void get_mail_conf_time_fn_table(table) /* CONFIG_INT_TABLE *table; /* AUXILIARY FUNCTIONS -/* int get_mail_conf_time2(name1, name2, defval, min, max); +/* int get_mail_conf_time2(name1, name2, defval, def_unit, min, max); /* const char *name1; /* const char *name2; /* const char *defval; +/* int def_unit; /* int min; /* int max; /* DESCRIPTION /* This module implements configuration parameter support -/* for time interval values. By default, times are specified -/* in units of seconds, but the conversion routines understand +/* for time interval values. The conversion routines understand /* one-letter suffixes to specify an explicit time unit: s /* (seconds), m (minutes), h (hours), d (days) or w (weeks). /* /* get_mail_conf_time() looks up the named entry in the global /* configuration dictionary. The default value is returned -/* when no value was found. +/* when no value was found. \fIdef_unit\fR supplies the default +/* time unit for numbers numbers specified without explicit unit. /* \fImin\fR is zero or specifies a lower limit on the integer /* value or string length; \fImax\fR is zero or specifies an /* upper limit on the integer value or string length. @@ -102,14 +105,17 @@ /* convert_mail_conf_time - look up and convert integer parameter value */ -static int convert_mail_conf_time(const char *name, int *intval) +static int convert_mail_conf_time(const char *name, int *intval, int def_unit) { const char *strval; char unit; char junk; if ((strval = mail_conf_lookup_eval(name)) != 0) { - if (sscanf(strval, "%d%c%c", intval, &unit, &junk) == 2) { + switch (sscanf(strval, "%d%c%c", intval, &unit, &junk)) { + case 1: + unit = def_unit; + case 2: switch (unit) { case 'w': *intval *= WEEK; @@ -125,14 +131,9 @@ static int convert_mail_conf_time(const char *name, int *intval) return (1); case 's': return (1); - break; - default: - msg_fatal("bad time unit: %s", strval); } } - if (sscanf(strval, "%d%c", intval, &junk) != 1) - msg_fatal("bad numerical configuration: %s = %s", name, strval); - return (1); + msg_fatal("bad time configuration: %s = %s", name, strval); } return (0); } @@ -149,13 +150,13 @@ static void check_mail_conf_time(const char *name, int intval, int min, int max) /* get_mail_conf_time - evaluate integer-valued configuration variable */ -int get_mail_conf_time(const char *name, const char *defval, int min, int max) +int get_mail_conf_time(const char *name, const char *defval, int def_unit, int min, int max) { int intval; - if (convert_mail_conf_time(name, &intval) == 0) + if (convert_mail_conf_time(name, &intval, def_unit) == 0) set_mail_conf_time(name, defval); - if (convert_mail_conf_time(name, &intval) == 0) + if (convert_mail_conf_time(name, &intval, def_unit) == 0) msg_panic("get_mail_conf_time: parameter not found: %s", name); check_mail_conf_time(name, intval, min, max); return (intval); @@ -164,15 +165,15 @@ int get_mail_conf_time(const char *name, const char *defval, int min, int ma /* get_mail_conf_time2 - evaluate integer-valued configuration variable */ int get_mail_conf_time2(const char *name1, const char *name2, - const char *defval, int min, int max) + const char *defval, int def_unit, int min, int max) { int intval; char *name; name = concatenate(name1, name2, (char *) 0); - if (convert_mail_conf_time(name, &intval) == 0) + if (convert_mail_conf_time(name, &intval, def_unit) == 0) set_mail_conf_time(name, defval); - if (convert_mail_conf_time(name, &intval) == 0) + if (convert_mail_conf_time(name, &intval, def_unit) == 0) msg_panic("get_mail_conf_time2: parameter not found: %s", name); check_mail_conf_time(name, intval, min, max); myfree(name); @@ -184,13 +185,13 @@ int get_mail_conf_time2(const char *name1, const char *name2, typedef const char *(*stupid_indent_time) (void); int get_mail_conf_time_fn(const char *name, stupid_indent_time defval, - int min, int max) + int def_unit, int min, int max) { int intval; - if (convert_mail_conf_time(name, &intval) == 0) + if (convert_mail_conf_time(name, &intval, def_unit) == 0) set_mail_conf_time(name, defval()); - if (convert_mail_conf_time(name, &intval) == 0) + if (convert_mail_conf_time(name, &intval, def_unit) == 0) msg_panic("get_mail_conf_time_fn: parameter not found: %s", name); check_mail_conf_time(name, intval, min, max); return (intval); @@ -209,7 +210,7 @@ void get_mail_conf_time_table(CONFIG_TIME_TABLE *table) { while (table->name) { table->target[0] = get_mail_conf_time(table->name, table->defval, - table->min, table->max); + table->def_unit, table->min, table->max); table++; } } @@ -220,7 +221,7 @@ void get_mail_conf_time_fn_table(CONFIG_TIME_FN_TABLE *table) { while (table->name) { table->target[0] = get_mail_conf_time_fn(table->name, table->defval, - table->min, table->max); + table->def_unit, table->min, table->max); table++; } } @@ -239,16 +240,31 @@ int main(int unused_argc, char **unused_argv) static int hours; static int days; static int weeks; - static CONFIG_TIME_TABLE time_table[] = { - "seconds", "10s", &seconds, 0, 0, - "minutes", "10m", &minutes, 0, 0, - "hours", "10h", &hours, 0, 0, - "days", "10d", &days, 0, 0, - "weeks", "10w", &weeks, 0, 0, + static CONFIG_TIME_TABLE time_table1[] = { + "seconds", "10s", &seconds, 's', 0, 0, + "minutes", "10m", &minutes, 'm', 0, 0, + "hours", "10h", &hours, 'h', 0, 0, + "days", "10d", &days, 'd', 0, 0, + "weeks", "10w", &weeks, 'w', 0, 0, + 0, + }; + static CONFIG_TIME_TABLE time_table2[] = { + "seconds", "10", &seconds, 's', 0, 0, + "minutes", "10", &minutes, 'm', 0, 0, + "hours", "10", &hours, 'h', 0, 0, + "days", "10", &days, 'd', 0, 0, + "weeks", "10", &weeks, 'w', 0, 0, 0, }; - get_mail_conf_time_table(time_table); + get_mail_conf_time_table(time_table1); + vstream_printf("seconds = %d\n", seconds); + vstream_printf("minutes = %d\n", minutes); + vstream_printf("hours = %d\n", hours); + vstream_printf("days = %d\n", days); + vstream_printf("weeks = %d\n", weeks); + + get_mail_conf_time_table(time_table2); vstream_printf("seconds = %d\n", seconds); vstream_printf("minutes = %d\n", minutes); vstream_printf("hours = %d\n", hours); diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 36a91dc5e..8ced2ecfa 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -15,7 +15,7 @@ * Version of this program. */ #define VAR_MAIL_VERSION "mail_version" -#define DEF_MAIL_VERSION "Snapshot-20001003" +#define DEF_MAIL_VERSION "Snapshot-20001004" extern char *var_mail_version; /* LICENSE diff --git a/postfix/src/nqmgr/Makefile.in b/postfix/src/nqmgr/Makefile.in index 6a006a7cc..376304e05 100644 --- a/postfix/src/nqmgr/Makefile.in +++ b/postfix/src/nqmgr/Makefile.in @@ -263,7 +263,6 @@ qmgr_scan.o: ../../include/msg.h qmgr_scan.o: ../../include/mymalloc.h qmgr_scan.o: ../../include/scan_dir.h qmgr_scan.o: ../../include/mail_scan_dir.h -qmgr_scan.o: ../../include/flush_clnt.h qmgr_scan.o: qmgr.h qmgr_scan.o: ../../include/vstream.h qmgr_scan.o: ../../include/vbuf.h diff --git a/postfix/src/nqmgr/qmgr_scan.c b/postfix/src/nqmgr/qmgr_scan.c index 71532fcfc..e0d9fc079 100644 --- a/postfix/src/nqmgr/qmgr_scan.c +++ b/postfix/src/nqmgr/qmgr_scan.c @@ -67,7 +67,6 @@ /* Global library. */ #include -#include /* Application-specific. */ @@ -101,14 +100,6 @@ static void qmgr_scan_start(QMGR_SCAN *scan_info) if (scan_info->nflags & QMGR_FLUSH_DEAD) qmgr_enable_all(); - /* - * Optionally inform the fast flush cache manager that we're attempting - * to deliver all queued mail. - */ - if ((scan_info->nflags & QMGR_FLUSH_DEAD) - && (scan_info->nflags & QMGR_SCAN_ALL)) - flush_purge(); - /* * Start or restart the scan. */ diff --git a/postfix/src/qmgr/Makefile.in b/postfix/src/qmgr/Makefile.in index 040e3b044..ad3320b48 100644 --- a/postfix/src/qmgr/Makefile.in +++ b/postfix/src/qmgr/Makefile.in @@ -235,7 +235,6 @@ qmgr_scan.o: ../../include/msg.h qmgr_scan.o: ../../include/mymalloc.h qmgr_scan.o: ../../include/scan_dir.h qmgr_scan.o: ../../include/mail_scan_dir.h -qmgr_scan.o: ../../include/flush_clnt.h qmgr_scan.o: qmgr.h qmgr_scan.o: ../../include/vstream.h qmgr_scan.o: ../../include/vbuf.h diff --git a/postfix/src/qmgr/qmgr_scan.c b/postfix/src/qmgr/qmgr_scan.c index 71532fcfc..e0d9fc079 100644 --- a/postfix/src/qmgr/qmgr_scan.c +++ b/postfix/src/qmgr/qmgr_scan.c @@ -67,7 +67,6 @@ /* Global library. */ #include -#include /* Application-specific. */ @@ -101,14 +100,6 @@ static void qmgr_scan_start(QMGR_SCAN *scan_info) if (scan_info->nflags & QMGR_FLUSH_DEAD) qmgr_enable_all(); - /* - * Optionally inform the fast flush cache manager that we're attempting - * to deliver all queued mail. - */ - if ((scan_info->nflags & QMGR_FLUSH_DEAD) - && (scan_info->nflags & QMGR_SCAN_ALL)) - flush_purge(); - /* * Start or restart the scan. */ diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c index 3f2dec418..62ba71dfa 100644 --- a/postfix/src/smtpd/smtpd.c +++ b/postfix/src/smtpd/smtpd.c @@ -1095,7 +1095,10 @@ static int etrn_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv) return (0); case FLUSH_STAT_BAD: msg_warn("bad ETRN %.100s... from %s", argv[1].strval, state->namaddr); + smtpd_chat_reply(state, "458 Unable to queue messages"); + return (-1); default: + msg_warn("unable to talk to fast flush service"); smtpd_chat_reply(state, "458 Unable to queue messages"); return (-1); }