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);
}