diff --git a/postfix/HISTORY b/postfix/HISTORY
index ef773ce67..a7f7bb214 100644
--- a/postfix/HISTORY
+++ b/postfix/HISTORY
@@ -5187,13 +5187,18 @@ Apologies for any names omitted.
20010522
- Feature: "postsuper -r queueID" re-queues a message. The
- message is moved to the maildrop queue so that the pickup
- daemon will copy it to a new file with the "right" name
- that matches the queue file inode number, and so that
- address rewriting will be done again. This is useful after
- changes of address rewriting or virtual mappings.
+ Feature: "postsuper -r queueID" re-queues a message, and
+ "postsuper -R" re-queues all mail. The message is moved to
+ the maildrop queue so that the pickup daemon will copy it
+ to a new queue file, and so that address rewriting will be
+ done again. This is useful after changes of address rewriting
+ or virtual mappings.
- Feature: "postsuper -R" re-queues all mail. This is useful
- after restoring a Postfix queue from another machine, or
- from backup.
+20010523
+
+ Feature: "postsuper -s" (which is done by default) renames
+ queue files whose queue ID does not match the message file
+ inode number.
+
+ Bugfix: memory leak in the LDAP client module. Alain
+ Thivillon,France Teaser - Groupe Firstream.
diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES
index 9fc8bffe8..3a7c71adb 100644
--- a/postfix/RELEASE_NOTES
+++ b/postfix/RELEASE_NOTES
@@ -12,20 +12,22 @@ server state just like RSET. This behavior cannot be disabled.
Major changes with snapshot-20010522
====================================
-Revision of some fine details in the light of the new RFC 2821 and
-RFC 2822 standards. Changes that may affect interoperability are
-listed above under "incompatible changes".
+This release contains revisions of some fine details in the light
+of the new RFC 2821 and RFC 2822 standards. Changes that may affect
+interoperability are listed above under "incompatible changes".
+
+The postsuper tool can rename files whose queue ID does not match
+the queue file inode number. This is necessary when a Postfix mail
+queue is restored from another machine or from backups. The feature
+is selected with the -s option, which is the default.
The postsuper queue maintenance tool was extended with options to
-read queue IDs from standard input (which makes it easier to drive
-the tool from scripts).
+read queue IDs from standard input. This makes the tool easier to
+drive from scripts.
The postsuper queue maintenance tool has a new -r (requeue) option
for subjecting queue files to another iteration of address rewriting.
-The postsuper -R option requeues all mail. This is necessary after
-restoring Postfix queues from another machine or from backups.
-
Major changes with snapshot-20010502
====================================
diff --git a/postfix/conf/postfix-script-nosgid b/postfix/conf/postfix-script-nosgid
index f84536675..7297df1e9 100755
--- a/postfix/conf/postfix-script-nosgid
+++ b/postfix/conf/postfix-script-nosgid
@@ -174,9 +174,6 @@ check)
\( -perm -020 -o -perm -002 \) \
-exec $WARN group or other writable: {} \;
- find $queue_directory/* $config_directory/* -name '*core' \
- -exec $WARN core file: {} \; 2>/dev/null
-
test -d maildrop || {
$WARN creating missing Postfix maildrop directory
mkdir maildrop || exit 1
@@ -254,10 +251,11 @@ EOF
$command_directory/postconf -e hash_queue_names="$found$missing"
}
- # See if all queue files are in the right place.
+ # See if all queue files are in the right place. This is slow.
+ # We must scan all queues for mis-named queue files before the
+ # mail system can run.
- $command_directory/postsuper active
- $command_directory/postsuper &
+ $command_directory/postsuper || exit 1
find corrupt -type f -exec $WARN damaged message: {} \;
diff --git a/postfix/conf/postfix-script-sgid b/postfix/conf/postfix-script-sgid
index 53bcf6303..98207c5e8 100755
--- a/postfix/conf/postfix-script-sgid
+++ b/postfix/conf/postfix-script-sgid
@@ -174,9 +174,6 @@ check)
\( -perm -020 -o -perm -002 \) \
-exec $WARN group or other writable: {} \;
- find $queue_directory/* $config_directory/* -name '*core' \
- -exec $WARN core file: {} \; 2>/dev/null
-
test -d maildrop || {
$WARN creating missing Postfix maildrop directory
mkdir maildrop || exit 1
@@ -255,10 +252,11 @@ EOF
$command_directory/postconf -e hash_queue_names="$found$missing"
}
- # See if all queue files are in the right place.
+ # See if all queue files are in the right place. This is slow.
+ # We must scan all queues for mis-named queue files before the
+ # mail system can run.
- $command_directory/postsuper active
- $command_directory/postsuper &
+ $command_directory/postsuper || exit 1
find corrupt -type f -exec $WARN damaged message: {} \;
diff --git a/postfix/html/faq.html b/postfix/html/faq.html
index 47f4d9ec5..011e7528e 100644
--- a/postfix/html/faq.html
+++ b/postfix/html/faq.html
@@ -3063,13 +3063,61 @@ that the file name will collide with another queue file.
-To avoid queue file name collisions when copying queue files,
-restore the incoming, active and deferred queue files under the
-maildrop directory instead.
+
+
Undefined symbols: ___dn_expand, ___res_init etc.
diff --git a/postfix/html/postsuper.1.html b/postfix/html/postsuper.1.html
index c608c6e01..724c28214 100644
--- a/postfix/html/postsuper.1.html
+++ b/postfix/html/postsuper.1.html
@@ -6,59 +6,59 @@ POSTSUPER(1) POSTSUPER(1)
NAME
- postsuper - Postfix super intendent
+ postsuper - Postfix superintendent
SYNOPSIS
postsuper [-psv] [-d queue_id] [-r queue_id] [directory
...]
DESCRIPTION
- The postsuper command does small maintenance jobs. Use of
- the command is restricted to the super-user.
+ The postsuper command does maintenance jobs on the Postfix
+ queue. Use of the command is restricted to the super-user.
- By default, postsuper performs the operations requested
- with the -s and -p command-line options on the named Post-
- fix queue directories (default: all). Directory names are
- relative to the Postfix top-level queue directory.
+ By default, postsuper performs the operations requested
+ with the -s and -p command-line options on all Postfix
+ queue directories - this includes the incoming, active and
+ deferred directories with mail files and the bounce, defer
+ and flush directories with log files.
Options:
- -d This option ignores any directory argument(s).
- Delete one message queue file with the named queue
- ID. Specify multiple -d options to delete multiple
- queue files by name.
-
- Alternatively, if a queue_id of - is specified, the
+ -d queue_id
+ Delete one message with the named queue ID from the
+ named mail queue(s) (default: incoming, active and
+ deferred). If a queue_id of - is specified, the
program reads queue IDs from standard input.
- The postsuper exit status is non-zero when no mes-
- sage queue file was deleted.
+ Specify -d ALL to remove all messages; for example,
+ specify -d ALL deferred to delete mail in the
+ deferred queue. As a safety measure, the word ALL
+ must be specified in upper case.
- There is a very small possibility that postsuper
- deletes the wrong message file when it is executed
- while the Postfix mail system is running.
+ Postfix queue IDs are reused. There is a very
+ small possibility that postsuper deletes the wrong
+ message file when it is executed while the Postfix
+ mail system is running.
The scenario is as follows:
- o The Postfix queue manager deletes the file
- that postsuper was supposed to delete,
- because Postfix was finished with the mes-
+ 1) The Postfix queue manager deletes the mes-
+ sage that postsuper is supposed to delete,
+ because Postfix is finished with the mes-
sage.
- o New mail arrives, and the new message is
+ 2) New mail arrives, and the new message is
given the same queue ID as the message that
- postsuper was supposed to delete. The prob-
- ability for reusing a deleted queue ID is
+ postsuper is supposed to delete. The proba-
+ bility for reusing a deleted queue ID is
about 1 in 2**15 (the number of different
microsecond values that the system clock can
distinguish within a second).
- o postsuper deletes the new message file,
- instead of the old file that should have
- been deleted.
+ 3) postsuper deletes the new message, instead
+ of the old message that it should have
+ deleted.
- -r This option ignores any directory argument(s).
- Requeue one message queue file with the named queue
@@ -71,33 +71,49 @@ POSTSUPER(1) POSTSUPER(1)
POSTSUPER(1) POSTSUPER(1)
- ID. Specify multiple -r options to requeue multi-
- ple queue files by name.
+ -p Purge old temporary files that are left over after
+ system or software crashes.
- Alternatively, if a queue_id of - is specified, the
- program reads queue IDs from standard input.
+ -r queue_id
+ Requeue the message with the named queue ID from
+ the named mail queue(s) (default: incoming, active
+ and deferred). To requeue multiple messages, spec-
+ ify multiple -r command-line options. Alterna-
+ tively, if a queue_id of - is specified, the pro-
+ gram reads queue IDs from standard input.
- The queue file is moved to the maildrop queue, from
- where it is copied by the pickup daemon to a new
- file whose name is guaranteed to match the queue
- file inode number. This feature is useful for queue
- files from another machine or for files restored
- from backup. The new queue file is subjected again
- to address rewriting and substitution.
+ Specify -r ALL to requeue all messages. As a safety
+ measure, the word ALL must be specified in upper
+ case.
- The postsuper exit status is non-zero when no mes-
- sage queue file was requeued.
+ A requeued message is moved to the maildrop queue,
+ from where it is copied by the pickup daemon to a
+ new file whose name is guaranteed to match the new
+ queue file inode number. The new queue file is sub-
+ jected again to mail address rewriting and substi-
+ tution. This is useful when rewriting rules or vir-
+ tual mappings have changed.
- -s Structure check. Move queue files that are in the
- wrong place in the file system hierarchy and remove
- subdirectories that are no longer needed. File
- rearrangements are necessary after a change in the
- hash_queue_names and/or hash_queue_depth configura-
- tion parameters. It is highly recommended to run
- this check once before Postfix startup.
+ Postfix queue IDs are reused. There is a very
+ small possibility that postsuper requeues the wrong
+ message file when it is executed while the Postfix
+ mail system is running, but no harm should be done.
- -p Purge stale files (files that are left over after
- system or software crashes).
+ -s Structure check and structure repair. It is highly
+ recommended to perform this operation once before
+ Postfix startup.
+
+ o Rename files whose name does not match the
+ message file inode number. This operation is
+ necessary after restoring a mail queue from
+ a different machine, or from backup media.
+
+ o Move queue files that are in the wrong place
+ in the file system hierarchy and remove sub-
+ directories that are no longer needed. File
+ position rearrangements are necessary after
+ a change in the hash_queue_names and/or
+ hash_queue_depth configuration parameters.
-v Enable verbose logging for debugging purposes. Mul-
tiple -v options make the software increasingly
@@ -107,6 +123,24 @@ POSTSUPER(1) POSTSUPER(1)
Problems are reported to the standard error stream and to
syslogd.
+ postsuper reports the number of messages deleted with -d,
+ the number of messages requeued with -r, and the number of
+
+
+
+ 2
+
+
+
+
+
+POSTSUPER(1) POSTSUPER(1)
+
+
+ messages whose queue file name was fixed with -s. The
+ report is written to the standard error stream and to sys-
+ logd.
+
CONFIGURATION PARAMETERS
See the Postfix main.cf file for syntax details and for
default values.
@@ -125,18 +159,6 @@ POSTSUPER(1) POSTSUPER(1)
AUTHOR(S)
Wietse Venema
IBM T.J. Watson Research
-
-
-
- 2
-
-
-
-
-
-POSTSUPER(1) POSTSUPER(1)
-
-
P.O. Box 704
Yorktown Heights, NY 10598, USA
@@ -151,28 +173,6 @@ POSTSUPER(1) POSTSUPER(1)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/postfix/man/man1/postsuper.1 b/postfix/man/man1/postsuper.1
index 4b5e83b43..80fc63864 100644
--- a/postfix/man/man1/postsuper.1
+++ b/postfix/man/man1/postsuper.1
@@ -4,7 +4,7 @@
.SH NAME
postsuper
\-
-Postfix super intendent
+Postfix superintendent
.SH SYNOPSIS
.na
.nf
@@ -14,27 +14,30 @@ Postfix super intendent
.SH DESCRIPTION
.ad
.fi
-The \fBpostsuper\fR command does small maintenance jobs. Use of
-the command is restricted to the super-user.
+The \fBpostsuper\fR command does maintenance jobs on the Postfix
+queue. Use of the command is restricted to the super-user.
By default, \fBpostsuper\fR performs the operations requested with the
-\fB-s\fR and \fB-p\fR command-line options on the named Postfix queue
-directories (default: all).
-Directory names are relative to the Postfix top-level queue directory.
+\fB-s\fR and \fB-p\fR command-line options on all Postfix queue
+directories - this includes the \fBincoming\fR, \fBactive\fR and
+\fBdeferred\fR directories with mail files and the \fBbounce\fR,
+\fBdefer\fR and \fBflush\fR directories with log files.
Options:
-.IP \fB-d \fIqueue_id\fR
-This option ignores any \fIdirectory\fR argument(s).
-Delete one message queue file with the named queue ID. Specify
-multiple \fB-d\fR options to delete multiple queue files by name.
+.IP "\fB-d \fIqueue_id\fR"
+Delete one message with the named queue ID from the named
+mail queue(s) (default: \fBincoming\fR, \fBactive\fR and
+\fBdeferred\fR).
+If a \fIqueue_id\fR of \fB-\fR is specified, the program reads
+queue IDs from standard input.
.sp
-Alternatively, if a \fIqueue_id\fR of \fB-\fR is specified, the
-program reads queue IDs from standard input.
-.sp
-The \fBpostsuper\fR exit status is non-zero when no message queue
-file was deleted.
+Specify \fB-d ALL\fR to remove all messages; for example, specify
+\fB-d ALL deferred\fR to delete mail in the \fBdeferred\fR queue.
+As a safety measure, the word \fBALL\fR must be specified in upper
+case.
.sp
.ft B
+Postfix queue IDs are reused.
There is a very small possibility that postsuper deletes the
wrong message file when it is executed while the Postfix mail
system is running.
@@ -42,47 +45,61 @@ system is running.
.sp
The scenario is as follows:
.RS
-.IP \(bu
-The Postfix queue manager deletes the file that \fBpostsuper\fR
-was supposed to delete, because Postfix was finished with the
+.IP 1)
+The Postfix queue manager deletes the message that \fBpostsuper\fR
+is supposed to delete, because Postfix is finished with the
message.
-.IP \(bu
+.IP 2)
New mail arrives, and the new message is given the same queue ID
-as the message that \fBpostsuper\fR was supposed to delete.
+as the message that \fBpostsuper\fR is supposed to delete.
The probability for reusing a deleted queue ID is about 1 in 2**15
(the number of different microsecond values that the system clock
can distinguish within a second).
-.IP \(bu
-\fBpostsuper\fR deletes the new message file, instead of the
-old file that should have been deleted.
+.IP 3)
+\fBpostsuper\fR deletes the new message, instead of the old
+message that it should have deleted.
.RE
-.IP \fB-r \fIqueue_id\fR
-This option ignores any \fIdirectory\fR argument(s).
-Requeue one message queue file with the named queue ID. Specify
-multiple \fB-r\fR options to requeue multiple queue files by name.
-.sp
-Alternatively, if a \fIqueue_id\fR of \fB-\fR is specified, the
-program reads queue IDs from standard input.
-.sp
-The queue file is moved to the maildrop queue, from where
-it is copied by the pickup daemon to a new file whose name
-is guaranteed to match the queue file inode number. This
-feature is useful for queue files from another machine or
-for files restored from backup. The new queue file is
-subjected again to address rewriting and substitution.
-.sp
-The \fBpostsuper\fR exit status is non-zero when no message queue
-file was requeued.
-.IP \fB-s\fR
-Structure check. Move queue files that are in the wrong place
-in the file system hierarchy and remove subdirectories that are
-no longer needed. File rearrangements are necessary after a change
-in the \fBhash_queue_names\fR and/or \fBhash_queue_depth\fR
-configuration parameters. It is highly recommended to run this
-check once before Postfix startup.
.IP \fB-p\fR
-Purge stale files (files that are left over after system or
-software crashes).
+Purge old temporary files that are left over after system or
+software crashes.
+.IP "\fB-r \fIqueue_id\fR"
+Requeue the message with the named queue ID from the named
+mail queue(s) (default: \fBincoming\fR, \fBactive\fR and
+\fBdeferred\fR).
+To requeue multiple messages, specify multiple \fB-r\fR
+command-line options.
+Alternatively, if a \fIqueue_id\fR of \fB-\fR is specified,
+the program reads queue IDs from standard input.
+.sp
+Specify \fB-r ALL\fR to requeue all messages. As a safety
+measure, the word \fBALL\fR must be specified in upper case.
+.sp
+A requeued message is moved to the \fBmaildrop\fR queue, from
+where it is copied by the pickup daemon to a new file whose name
+is guaranteed to match the new queue file inode number. The
+new queue file is subjected again to mail address rewriting and
+substitution. This is useful when rewriting rules or virtual
+mappings have changed.
+.sp
+Postfix queue IDs are reused.
+There is a very small possibility that \fBpostsuper\fR requeues
+the wrong message file when it is executed while the Postfix mail
+system is running, but no harm should be done.
+.IP \fB-s\fR
+Structure check and structure repair. It is highly recommended
+to perform this operation once before Postfix startup.
+.RS
+.IP \(bu
+Rename files whose name does not match the message file inode
+number. This operation is necessary after restoring a mail queue
+from a different machine, or from backup media.
+.IP \(bu
+Move queue files that are in the wrong place in the file system
+hierarchy and remove subdirectories that are no longer needed.
+File position rearrangements are necessary after a change in the
+\fBhash_queue_names\fR and/or \fBhash_queue_depth\fR
+configuration parameters.
+.RE
.IP \fB-v\fR
Enable verbose logging for debugging purposes. Multiple \fB-v\fR
options make the software increasingly verbose.
@@ -91,6 +108,11 @@ options make the software increasingly verbose.
.fi
Problems are reported to the standard error stream and to
\fBsyslogd\fR.
+
+\fBpostsuper\fR reports the number of messages deleted with \fB-d\fR,
+the number of messages requeued with \fB-r\fR, and the number of
+messages whose queue file name was fixed with \fB-s\fR. The report
+is written to the standard error stream and to \fBsyslogd\fR.
.SH CONFIGURATION PARAMETERS
.na
.nf
diff --git a/postfix/src/global/file_id.c b/postfix/src/global/file_id.c
index ad4ce5798..e130087db 100644
--- a/postfix/src/global/file_id.c
+++ b/postfix/src/global/file_id.c
@@ -65,7 +65,7 @@ const char *get_file_id(int fd)
result = vstring_alloc(1);
if (fstat(fd, &st) < 0)
msg_fatal("fstat: %m");
- vstring_sprintf(result, "%X", (int) st.st_ino);
+ vstring_sprintf(result, "%lX", (long) st.st_ino);
return (vstring_str(result));
}
diff --git a/postfix/src/global/mail_queue.c b/postfix/src/global/mail_queue.c
index a8c3673c3..9e8e64323 100644
--- a/postfix/src/global/mail_queue.c
+++ b/postfix/src/global/mail_queue.c
@@ -388,11 +388,13 @@ VSTREAM *mail_queue_enter(const char *queue_name, int mode)
for (count = 0;; count++) {
vstring_sprintf(id_buf, "%05X%s", (int) tv.tv_usec, file_id);
mail_queue_path(path_buf, queue_name, STR(id_buf));
+#if 0
if (access(STR(path_buf), X_OK) == 0) { /* collision. */
if ((int) ++tv.tv_usec < 0)
tv.tv_usec = 0;
continue;
}
+#endif
if (sane_rename(STR(temp_path), STR(path_buf)) == 0) /* success */
break;
if (errno == EPERM || errno == EISDIR) {/* collision. weird. */
diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h
index 7e53c391a..a667503e8 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-20010522"
+#define DEF_MAIL_VERSION "Snapshot-20010524"
extern char *var_mail_version;
/* LICENSE
diff --git a/postfix/src/postsuper/postsuper.c b/postfix/src/postsuper/postsuper.c
index da6ab3fd9..80ee5c30c 100644
--- a/postfix/src/postsuper/postsuper.c
+++ b/postfix/src/postsuper/postsuper.c
@@ -2,33 +2,36 @@
/* NAME
/* postsuper 1
/* SUMMARY
-/* Postfix super intendent
+/* Postfix superintendent
/* SYNOPSIS
/* .fi
-/* \fBpostsuper\fR [\fB-Rpsv\fR] [\fB-d \fIqueue_id\fR]
+/* \fBpostsuper\fR [\fB-psv\fR] [\fB-d \fIqueue_id\fR]
/* [\fB-r \fIqueue_id\fR] [\fIdirectory ...\fR]
/* DESCRIPTION
-/* The \fBpostsuper\fR command does small maintenance jobs. Use of
-/* the command is restricted to the super-user.
+/* The \fBpostsuper\fR command does maintenance jobs on the Postfix
+/* queue. Use of the command is restricted to the super-user.
/*
/* By default, \fBpostsuper\fR performs the operations requested with the
-/* \fB-s\fR and \fB-p\fR command-line options on the named Postfix queue
-/* directories (default: all).
-/* Directory names are relative to the Postfix top-level queue directory.
+/* \fB-s\fR and \fB-p\fR command-line options on all Postfix queue
+/* directories - this includes the \fBincoming\fR, \fBactive\fR and
+/* \fBdeferred\fR directories with mail files and the \fBbounce\fR,
+/* \fBdefer\fR and \fBflush\fR directories with log files.
/*
/* Options:
-/* .IP \fB-d \fIqueue_id\fR
-/* This option ignores any \fIdirectory\fR argument(s).
-/* Delete one message queue file with the named queue ID. Specify
-/* multiple \fB-d\fR options to delete multiple queue files by name.
+/* .IP "\fB-d \fIqueue_id\fR"
+/* Delete one message with the named queue ID from the named
+/* mail queue(s) (default: \fBincoming\fR, \fBactive\fR and
+/* \fBdeferred\fR).
+/* If a \fIqueue_id\fR of \fB-\fR is specified, the program reads
+/* queue IDs from standard input.
/* .sp
-/* Alternatively, if a \fIqueue_id\fR of \fB-\fR is specified, the
-/* program reads queue IDs from standard input.
-/* .sp
-/* The \fBpostsuper\fR exit status is non-zero when no message queue
-/* file was deleted.
+/* Specify \fB-d ALL\fR to remove all messages; for example, specify
+/* \fB-d ALL deferred\fR to delete mail in the \fBdeferred\fR queue.
+/* As a safety measure, the word \fBALL\fR must be specified in upper
+/* case.
/* .sp
/* .ft B
+/* Postfix queue IDs are reused.
/* There is a very small possibility that postsuper deletes the
/* wrong message file when it is executed while the Postfix mail
/* system is running.
@@ -36,64 +39,72 @@
/* .sp
/* The scenario is as follows:
/* .RS
-/* .IP \(bu
-/* The Postfix queue manager deletes the file that \fBpostsuper\fR
-/* was supposed to delete, because Postfix was finished with the
+/* .IP 1)
+/* The Postfix queue manager deletes the message that \fBpostsuper\fR
+/* is supposed to delete, because Postfix is finished with the
/* message.
-/* .IP \(bu
+/* .IP 2)
/* New mail arrives, and the new message is given the same queue ID
-/* as the message that \fBpostsuper\fR was supposed to delete.
+/* as the message that \fBpostsuper\fR is supposed to delete.
/* The probability for reusing a deleted queue ID is about 1 in 2**15
/* (the number of different microsecond values that the system clock
/* can distinguish within a second).
-/* .IP \(bu
-/* \fBpostsuper\fR deletes the new message file, instead of the
-/* old file that should have been deleted.
+/* .IP 3)
+/* \fBpostsuper\fR deletes the new message, instead of the old
+/* message that it should have deleted.
/* .RE
-/* .IP \fB-R\fR
-/* This option ignores any \fIdirectory\fR argument(s).
-/* Requeue all message queue files. This option is useful for
-/* restoring a Postfix queue from another machine or from backup.
-/* .sp
-/* Each queue file is moved to the maildrop queue, from where
-/* it is copied by the pickup daemon to a new file whose name
-/* is guaranteed to match the queue file inode number. The
-/* new queue file is subjected again to address rewriting and
-/* substitution. This is useful when rewriting rules or virtual
-/* mappings have changed.
-/* .IP \fB-r \fIqueue_id\fR
-/* This option ignores any \fIdirectory\fR argument(s).
-/* Requeue one message queue file with the named queue ID. Specify
-/* multiple \fB-r\fR options to requeue multiple queue files by name.
-/* .sp
-/* Alternatively, if a \fIqueue_id\fR of \fB-\fR is specified, the
-/* program reads queue IDs from standard input.
-/* .sp
-/* The queue file is moved to the maildrop queue, from where
-/* it is copied by the pickup daemon to a new file whose name
-/* is guaranteed to match the queue file inode number. The
-/* new queue file is subjected again to address rewriting and
-/* substitution. This is useful when rewriting rules or virtual
-/* mappings have changed.
-/* .sp
-/* The \fBpostsuper\fR exit status is non-zero when no message queue
-/* file was requeued.
-/* .IP \fB-s\fR
-/* Structure check. Move queue files that are in the wrong place
-/* in the file system hierarchy and remove subdirectories that are
-/* no longer needed. File rearrangements are necessary after a change
-/* in the \fBhash_queue_names\fR and/or \fBhash_queue_depth\fR
-/* configuration parameters. It is highly recommended to run this
-/* check once before Postfix startup.
/* .IP \fB-p\fR
-/* Purge stale files (files that are left over after system or
-/* software crashes).
+/* Purge old temporary files that are left over after system or
+/* software crashes.
+/* .IP "\fB-r \fIqueue_id\fR"
+/* Requeue the message with the named queue ID from the named
+/* mail queue(s) (default: \fBincoming\fR, \fBactive\fR and
+/* \fBdeferred\fR).
+/* To requeue multiple messages, specify multiple \fB-r\fR
+/* command-line options.
+/* Alternatively, if a \fIqueue_id\fR of \fB-\fR is specified,
+/* the program reads queue IDs from standard input.
+/* .sp
+/* Specify \fB-r ALL\fR to requeue all messages. As a safety
+/* measure, the word \fBALL\fR must be specified in upper case.
+/* .sp
+/* A requeued message is moved to the \fBmaildrop\fR queue, from
+/* where it is copied by the pickup daemon to a new file whose name
+/* is guaranteed to match the new queue file inode number. The
+/* new queue file is subjected again to mail address rewriting and
+/* substitution. This is useful when rewriting rules or virtual
+/* mappings have changed.
+/* .sp
+/* Postfix queue IDs are reused.
+/* There is a very small possibility that \fBpostsuper\fR requeues
+/* the wrong message file when it is executed while the Postfix mail
+/* system is running, but no harm should be done.
+/* .IP \fB-s\fR
+/* Structure check and structure repair. It is highly recommended
+/* to perform this operation once before Postfix startup.
+/* .RS
+/* .IP \(bu
+/* Rename files whose name does not match the message file inode
+/* number. This operation is necessary after restoring a mail queue
+/* from a different machine, or from backup media.
+/* .IP \(bu
+/* Move queue files that are in the wrong place in the file system
+/* hierarchy and remove subdirectories that are no longer needed.
+/* File position rearrangements are necessary after a change in the
+/* \fBhash_queue_names\fR and/or \fBhash_queue_depth\fR
+/* configuration parameters.
+/* .RE
/* .IP \fB-v\fR
/* Enable verbose logging for debugging purposes. Multiple \fB-v\fR
/* options make the software increasingly verbose.
/* DIAGNOSTICS
/* Problems are reported to the standard error stream and to
/* \fBsyslogd\fR.
+/*
+/* \fBpostsuper\fR reports the number of messages deleted with \fB-d\fR,
+/* the number of messages requeued with \fB-r\fR, and the number of
+/* messages whose queue file name was fixed with \fB-s\fR. The report
+/* is written to the standard error stream and to \fBsyslogd\fR.
/* CONFIGURATION PARAMETERS
/* .ad
/* .fi
@@ -123,6 +134,7 @@
#include
#include
#include
+#include
#include /* remove() */
/* Utility library. */
@@ -155,9 +167,10 @@
#define ACTION_STRUCT (1<<0) /* fix file organization */
#define ACTION_PURGE (1<<1) /* purge old temp files */
-#define ACTION_DELETE (1<<2) /* delete named queue file(s) */
-#define ACTION_REQUEUE (1<<3) /* requeue named queue file(s) */
-#define ACTION_REQUEUE_ALL (1<<4) /* requeue all queue file(s) */
+#define ACTION_DELETE_ONE (1<<2) /* delete named queue file(s) */
+#define ACTION_DELETE_ALL (1<<3) /* delete all queue file(s) */
+#define ACTION_REQUEUE_ONE (1<<4) /* requeue named queue file(s) */
+#define ACTION_REQUEUE_ALL (1<<5) /* requeue all queue file(s) */
#define ACTION_DEFAULT (ACTION_STRUCT | ACTION_PURGE)
@@ -188,78 +201,144 @@ static struct queue_info queue_info[] = {
0,
};
-/* postunlink - remove file with prejudice */
+ /*
+ * Directories with per-message meta files.
+ */
+const char *log_queue_names[] = {
+ MAIL_QUEUE_BOUNCE,
+ MAIL_QUEUE_DEFER,
+ 0,
+};
-static int postunlink(const char *path)
+ /*
+ * Cruft that we append to a file name when a queue ID is named after the
+ * message file inode number. This cruft must not pass mail_queue_id_ok() so
+ * that the queue manager will ignore it, should people be so unwise as to
+ * run this operation on a live mail system.
+ */
+#define SUFFIX "#FIX"
+#define SUFFIX_LEN 4
+
+ /*
+ * Grr. These counters are global, because C only has clumsy ways to return
+ * multiple results from a function.
+ */
+static int message_requeued = 0; /* requeued messages */
+static int message_deleted = 0; /* deleted messages */
+static int inode_fixed = 0; /* queue id matched to inode number */
+static int inode_mismatch = 0; /* queue id inode mismatch */
+static int position_mismatch = 0; /* file position mismatch */
+
+ /*
+ * Silly little macros. These translate arcane expressions into something
+ * more at a conceptual level.
+ */
+#define MESSAGE_QUEUE(qp) ((qp)->perms == MAIL_QUEUE_STAT_READY)
+#define READY_MESSAGE(st) (((st).st_mode & S_IRWXU) == MAIL_QUEUE_STAT_READY)
+
+/* find_queue_info - look up expected permissions field by queue name */
+
+static struct queue_info *find_queue_info(const char *queue_name)
+{
+ struct queue_info *qp;
+
+ for (qp = queue_info; qp->name; qp++)
+ if (strcmp(queue_name, qp->name) == 0)
+ return (qp);
+ msg_fatal("invalid directory name: %s", queue_name);
+}
+
+/* postremove - remove file with extreme prejudice */
+
+static int postremove(const char *path)
{
int ret;
- if ((ret = unlink(path)) == 0) {
- msg_info("removed file %s", path);
- } else if (errno != ENOENT) {
- msg_warn("remove file %s: %m", path);
- } else if (msg_verbose) {
- msg_info("remove file %s: %m", path);
+ if ((ret = remove(path)) < 0) {
+ if (errno != ENOENT)
+ msg_fatal("remove file %s: %m", path);
+ } else {
+ if (msg_verbose)
+ msg_info("removed file %s", path);
}
return (ret);
}
-/* postrename - rename file with prejudice */
+/* postrename - rename file with extreme prejudice */
static int postrename(const char *old, const char *new)
{
int ret;
- if ((ret = sane_rename(old, new)) == 0) {
- msg_info("requeued file %s as %s", old, new);
- } else if (errno != ENOENT) {
- msg_warn("requeue file %s as %s: %m", old, new);
- } else if (msg_verbose) {
- msg_info("requeue file %s as %s: %m", old, new);
+ if ((ret = sane_rename(old, new)) < 0) {
+ if (errno != ENOENT
+ || mail_queue_mkdirs(new) < 0
+ || sane_rename(old, new) < 0)
+ if (errno != ENOENT)
+ msg_fatal("rename file %s as %s: %m", old, new);
+ } else {
+ if (msg_verbose)
+ msg_info("renamed file %s as %s", old, new);
+ }
+ return (ret);
+}
+
+/* postrmdir - remove directory with extreme prejudice */
+
+static int postrmdir(const char *path)
+{
+ int ret;
+
+ if ((ret = rmdir(path)) < 0) {
+ if (errno != ENOENT)
+ msg_fatal("remove directory %s: %m", path);
+ } else {
+ if (msg_verbose)
+ msg_info("remove directory %s", path);
}
return (ret);
}
/* delete_one - delete one message instance and all its associated files */
-static int delete_one(const char *queue_id)
+static int delete_one(const char **queue_names, const char *queue_id)
{
- const char *msg_queue_names[] = {
- MAIL_QUEUE_MAILDROP,
- MAIL_QUEUE_INCOMING, /* twice, to avoid */
- MAIL_QUEUE_ACTIVE, /* missing a file while */
- MAIL_QUEUE_DEFERRED, /* it is being renamed */
- MAIL_QUEUE_INCOMING, /* this is not 100% */
- MAIL_QUEUE_ACTIVE, /* foolproof but adequate */
- 0,
- };
- const char *log_queue_names[] = {
- MAIL_QUEUE_BOUNCE,
- MAIL_QUEUE_DEFER,
- 0,
- };
struct stat st;
const char **msg_qpp;
const char **log_qpp;
const char *msg_path;
- VSTRING *log_path_buf = vstring_alloc(100);
- int found = 0;
+ VSTRING *log_path_buf;
+ int found;
+ int tries;
/*
- * Delete defer or bounce logfiles before deleting the corresponding
- * message file, and only if the message file exists. This minimizes but
- * does not eliminate a race condition with queue ID reuse which results
- * in deleting the wrong files.
+ * Sanity check. No early returns beyond this point.
*/
- for (msg_qpp = msg_queue_names; *msg_qpp != 0; msg_qpp++) {
- if (mail_open_ok(*msg_qpp, queue_id, &st, &msg_path) != MAIL_OPEN_YES)
- continue;
- for (log_qpp = log_queue_names; *log_qpp != 0; log_qpp++)
- postunlink(mail_queue_path(log_path_buf, *log_qpp, queue_id));
- if (postunlink(msg_path) == 0) {
- found = 1;
- break;
- } /* else: lost a race */
+ if (!mail_queue_id_ok(queue_id)) {
+ msg_warn("invalid mail queue id: %s", queue_id);
+ return (0);
+ }
+ log_path_buf = vstring_alloc(100);
+
+ /*
+ * Skip meta file directories. Delete defer or bounce logfiles before
+ * deleting the corresponding message file, and only if the message file
+ * exists. This minimizes but does not eliminate a race condition with
+ * queue ID reuse which results in deleting the wrong files.
+ */
+ for (found = 0, tries = 0; found == 0 && tries < 2; tries++) {
+ for (msg_qpp = queue_names; *msg_qpp != 0; msg_qpp++) {
+ if (!MESSAGE_QUEUE(find_queue_info(*msg_qpp)))
+ continue;
+ if (mail_open_ok(*msg_qpp, queue_id, &st, &msg_path) != MAIL_OPEN_YES)
+ continue;
+ for (log_qpp = log_queue_names; *log_qpp != 0; log_qpp++)
+ postremove(mail_queue_path(log_path_buf, *log_qpp, queue_id));
+ if (postremove(msg_path) == 0) {
+ found = 1;
+ break;
+ } /* else: maybe lost a race */
+ }
}
vstring_free(log_path_buf);
return (found);
@@ -267,35 +346,44 @@ static int delete_one(const char *queue_id)
/* requeue_one - requeue one message instance and delete its logfiles */
-static int requeue_one(const char *queue_id)
+static int requeue_one(const char **queue_names, const char *queue_id)
{
- const char *msg_queue_names[] = {
- MAIL_QUEUE_INCOMING, /* twice, to avoid */
- MAIL_QUEUE_ACTIVE, /* missing a file while */
- MAIL_QUEUE_DEFERRED, /* it is being renamed */
- MAIL_QUEUE_INCOMING, /* this is not 100% */
- MAIL_QUEUE_ACTIVE, /* foolproof but adequate */
- 0,
- };
struct stat st;
const char **msg_qpp;
const char *old_path;
- VSTRING *new_path_buf = vstring_alloc(100);
- int found = 0;
+ VSTRING *new_path_buf;
+ int found;
+ int tries;
/*
- * Do not delete defer or bounce logfiles, to avoid losing a race where
- * the queue manager decides to bounce mail after all recipients have
- * been tried.
+ * Sanity check. No early returns beyond this point.
*/
- for (msg_qpp = msg_queue_names; *msg_qpp != 0; msg_qpp++) {
- if (mail_open_ok(*msg_qpp, queue_id, &st, &old_path) != MAIL_OPEN_YES)
- continue;
- (void) mail_queue_path(new_path_buf, MAIL_QUEUE_MAILDROP, queue_id);
- if (postrename(old_path, STR(new_path_buf)) == 0) {
- found = 1;
- break;
- } /* else: lost a race */
+ if (!mail_queue_id_ok(queue_id)) {
+ msg_warn("invalid mail queue id: %s", queue_id);
+ return (0);
+ }
+ new_path_buf = vstring_alloc(100);
+
+ /*
+ * Skip meta file directories. Like the mass requeue operation, we not
+ * delete defer or bounce logfiles, to avoid losing a race where the
+ * queue manager decides to bounce mail after all recipients have been
+ * tried.
+ */
+ for (found = 0, tries = 0; found == 0 && tries < 2; tries++) {
+ for (msg_qpp = queue_names; *msg_qpp != 0; msg_qpp++) {
+ if (strcmp(*msg_qpp, MAIL_QUEUE_MAILDROP) == 0)
+ continue;
+ if (!MESSAGE_QUEUE(find_queue_info(*msg_qpp)))
+ continue;
+ if (mail_open_ok(*msg_qpp, queue_id, &st, &old_path) != MAIL_OPEN_YES)
+ continue;
+ (void) mail_queue_path(new_path_buf, MAIL_QUEUE_MAILDROP, queue_id);
+ if (postrename(old_path, STR(new_path_buf)) == 0) {
+ found = 1;
+ break;
+ } /* else: maybe lost a race */
+ }
}
vstring_free(new_path_buf);
return (found);
@@ -303,33 +391,84 @@ static int requeue_one(const char *queue_id)
/* operate_stream - operate on queue IDs given on stream */
-static int operate_stream(VSTREAM *fp, int (*operator) (const char *))
+static int operate_stream(VSTREAM *fp,
+ int (*operator) (const char **, const char *),
+ const char **queues)
{
VSTRING *buf = vstring_alloc(20);
int found = 0;
while (vstring_get_nonl(buf, fp) != VSTREAM_EOF)
- found |= operator(STR(buf));
+ found += operator(queues, STR(buf));
vstring_free(buf);
return (found);
}
-/* super - check queue file location and clean up */
+/* fix_queue_id - make message queue ID match inode number */
-static void super(char **queues, int action)
+static int fix_queue_id(const char *actual_path, const char *actual_queue,
+ const char *actual_id, ino_t inum)
+{
+ VSTRING *old_path = vstring_alloc(10);
+ VSTRING *new_path = vstring_alloc(10);
+ VSTRING *new_id = vstring_alloc(10);
+ const char **log_qpp;
+ int ret;
+
+ /*
+ * Create the new queue ID from the existing time digits and from the new
+ * inode number. Since we are renaming multiple files, the new name must
+ * be deterministic so that we can recover even when the renaming
+ * operation is interrupted in the middle.
+ */
+ vstring_sprintf(new_id, "%.5s%lX", actual_id, (unsigned long) inum);
+
+ /*
+ * Rename logfiles before renaming the message file, so that we can
+ * recover when a previous attempt was interrupted.
+ */
+ for (log_qpp = log_queue_names; *log_qpp; log_qpp++) {
+ mail_queue_path(old_path, *log_qpp, actual_id);
+ mail_queue_path(new_path, *log_qpp, STR(new_id));
+ vstring_strcat(new_path, SUFFIX);
+ postrename(STR(old_path), STR(new_path));
+ }
+
+ /*
+ * Rename the message file last, so that we know that we are done with
+ * this message and with all its logfiles.
+ */
+ mail_queue_path(new_path, actual_queue, STR(new_id));
+ vstring_strcat(new_path, SUFFIX);
+ ret = postrename(actual_path, STR(new_path));
+
+ /*
+ * Clean up.
+ */
+ vstring_free(old_path);
+ vstring_free(new_path);
+ vstring_free(new_id);
+
+ return (ret);
+}
+
+/* super - check queue structure, clean up, do wild-card operations */
+
+static void super(const char **queues, int action)
{
ARGV *hash_queue_names = argv_split(var_hash_queue_names, " \t\r\n,");
VSTRING *actual_path = vstring_alloc(10);
VSTRING *wanted_path = vstring_alloc(10);
struct stat st;
- char *queue_name;
+ const char *queue_name;
SCAN_DIR *info;
char *path;
int actual_depth;
int wanted_depth;
char **cpp;
struct queue_info *qp;
+ unsigned long inum;
/*
* Make sure every file is in the right place, clean out stale files, and
@@ -345,12 +484,7 @@ static void super(char **queues, int action)
* file permissions to look for, and whether or not it is desirable
* to step into subdirectories.
*/
- for (qp = queue_info; /* void */ ; qp++) {
- if (qp->name == 0)
- msg_fatal("unknown queue name: %s", queue_name);
- if (strcmp(qp->name, queue_name) == 0)
- break;
- }
+ qp = find_queue_info(queue_name);
for (cpp = hash_queue_names->argv; /* void */ ; cpp++) {
if (*cpp == 0) {
wanted_depth = 0;
@@ -383,12 +517,8 @@ static void super(char **queues, int action)
if ((path = scan_dir_next(info)) == 0) {
if (actual_depth == 0)
break;
- if (actual_depth > wanted_depth) {
- if (rmdir(scan_dir_path(info)) < 0 && errno != ENOENT)
- msg_warn("remove %s: %m", scan_dir_path(info));
- else if (msg_verbose)
- msg_info("remove %s", scan_dir_path(info));
- }
+ if (actual_depth > wanted_depth)
+ postrmdir(scan_dir_path(info));
scan_dir_pop(info);
actual_depth--;
continue;
@@ -406,83 +536,174 @@ static void super(char **queues, int action)
}
/*
- * See if this is a stale file or some non-file object. Be
- * careful not to delete bounce or defer logs just because they
- * are more than a couple days old.
+ * From here on we need to keep track of operations that
+ * invalidate or revalidate the actual_path and path variables,
+ * otherwise we can hit the wrong files.
*/
vstring_sprintf(actual_path, "%s/%s", scan_dir_path(info), path);
if (stat(STR(actual_path), &st) < 0)
continue;
+
+ /*
+ * Remove alien directories. If maildrop is world writable, then
+ * we cannot abort just because we cannot remove someone's
+ * directory.
+ */
if (S_ISDIR(st.st_mode)) {
- if (rmdir(STR(actual_path)) < 0 && errno != ENOENT)
- msg_warn("remove subdirectory %s: %m", STR(actual_path));
- else if (msg_verbose)
- msg_info("remove subdirectory %s", STR(actual_path));
- continue;
- }
- if (!S_ISREG(st.st_mode)
- || ((action & ACTION_PURGE) != 0 &&
- (st.st_mode & S_IRWXU) != qp->perms
- && time((time_t *) 0) > st.st_mtime + MAX_TEMP_AGE)) {
- if (remove(STR(actual_path)) < 0 && errno != ENOENT)
- msg_warn("remove %s: %m", STR(actual_path));
- else if (msg_verbose)
- msg_info("remove %s", STR(actual_path));
- continue;
- }
-
- /*
- * Skip temporary files that aren't old enough.
- */
- if (mail_queue_id_ok(path) == 0)
- continue;
-
- /*
- * Requeue this message. This means the pickup daemon will copy
- * it to a new queue file, and that address rewriting is applied
- * again. XXX Share more code with requeue_one(). Note, that this
- * option is intended for large-scale mail queue restore
- * operations so that at this stage, the queue file may not even
- * be in the "right" place for the current machine. We therefore
- * cannot rely on the mail_queue(3) API.()
- */
- if ((action & ACTION_REQUEUE_ALL)
- && strcmp(queue_name, MAIL_QUEUE_MAILDROP) != 0) {
- (void) mail_queue_path(wanted_path, MAIL_QUEUE_MAILDROP, path);
- if (rename(STR(actual_path), STR(wanted_path)) < 0) {
+ if (rmdir(STR(actual_path)) < 0) {
if (errno != ENOENT)
- msg_fatal("rename %s to %s: %m", STR(actual_path),
- STR(wanted_path));
+ msg_warn("remove subdirectory %s: %m", STR(actual_path));
} else {
if (msg_verbose)
- msg_info("rename %s to %s", STR(actual_path),
- STR(wanted_path));
+ msg_info("remove subdirectory %s", STR(actual_path));
}
+ /* No further work on this object is possible. */
continue;
}
+ /*
+ * Mass deletion. We count the deletion of mail that this system
+ * has taken responsibility for. XXX This option does not use the
+ * mail_queue(3) API, so that it can be run on a queue that does
+ * not have files in the "right" place. It would be terribly
+ * inefficient to first have to rename files into place before
+ * deleting them.
+ */
+ if (action & ACTION_DELETE_ALL) {
+ if (postremove(STR(actual_path)) == 0)
+ if (MESSAGE_QUEUE(qp) && READY_MESSAGE(st))
+ message_deleted++;
+ /* No further work on this object is possible. */
+ continue;
+ }
+
+ /*
+ * Remove non-file objects and old temporary files. Be careful
+ * not to delete bounce or defer logs just because they are more
+ * than a couple days old.
+ */
+ if (!S_ISREG(st.st_mode)
+ || ((action & ACTION_PURGE) != 0
+ && MESSAGE_QUEUE(qp)
+ && !READY_MESSAGE(st)
+ && time((time_t *) 0) > st.st_mtime + MAX_TEMP_AGE)) {
+ (void) postremove(STR(actual_path));
+ /* No further work on this object is possible. */
+ continue;
+ }
+
+ /*
+ * Skip temporary message files that aren't old enough.
+ */
+ if (MESSAGE_QUEUE(qp) && !READY_MESSAGE(st))
+ /* No further work on this object is possible. */
+ continue;
+
+ /*
+ * Fix queueid#FIX names that were left from a previous pass over
+ * the queue where message queue file names were matched to their
+ * inode number. This requires that the file is already in the
+ * proper directory so that we only have to strip the suffix.
+ * Make sure that the name minus suffix is well formed and that
+ * the name matches the file inode number.
+ */
+ if (strcmp(path + (strlen(path) - SUFFIX_LEN), SUFFIX) == 0) {
+ path[strlen(path) - SUFFIX_LEN] = 0; /* XXX */
+ if (!mail_queue_id_ok(path)) {
+ msg_warn("bogus file name: %s", STR(actual_path));
+ continue;
+ }
+ if (MESSAGE_QUEUE(qp)) {
+ if (sscanf(path + 5, "%lx", &inum) != 1) {
+ msg_warn("bogus file name: %s", STR(actual_path));
+ continue;
+ }
+ if (inum != (unsigned long) st.st_ino) {
+ msg_warn("name/inode mismatch: %s", STR(actual_path));
+ continue;
+ }
+ }
+ vstring_strncpy(wanted_path, STR(actual_path),
+ strlen(STR(actual_path)) - SUFFIX_LEN);
+ if (postrename(STR(actual_path), STR(wanted_path)) < 0) {
+ /* No further work on this object is possible. */
+ continue;
+ } else {
+ if (MESSAGE_QUEUE(qp))
+ inode_fixed++;
+ vstring_strcpy(actual_path, STR(wanted_path));
+ /* At this point, path and actual_path are revalidated. */
+ }
+ }
+
+ /*
+ * Skip over files with illegal names. The library routines
+ * refuse to operate on them.
+ */
+ if (!mail_queue_id_ok(path)) {
+ msg_warn("bogus file name: %s", STR(actual_path));
+ continue;
+ }
+
+ /*
+ * Mass requeuing. The pickup daemon will copy requeued mail to a
+ * new queue file, so that address rewriting is applied again.
+ * XXX This option does not use the mail_queue(3) API, so that it
+ * can be run on a queue that does not have the files in the
+ * "right" place. It would be terribly inefficient to first have
+ * to rename files into place before requeuing them. Like the
+ * requeue_one() routine, this code does not touch logfiles.
+ */
+ if ((action & ACTION_REQUEUE_ALL)
+ && MESSAGE_QUEUE(qp)
+ && strcmp(queue_name, MAIL_QUEUE_MAILDROP) != 0) {
+ (void) mail_queue_path(wanted_path, MAIL_QUEUE_MAILDROP, path);
+ if (postrename(STR(actual_path), STR(wanted_path)) == 0)
+ message_requeued++;
+ /* At this point, path and actual_path are invalidated. */
+ continue;
+ }
+
+ /*
+ * See if the file name matches the file inode number. Skip meta
+ * file directories. This option requires that meta files be put
+ * into their proper place before queue files, so that we can
+ * rename queue files and meta files at the same time. Mis-named
+ * files are renamed to newqueueid#FIX on the first pass, and
+ * upon the second pass the #FIX is stripped off the name. Of
+ * course we have to be prepared that the program is interrupted
+ * before it completes, so any left-over newqueueid#FIX files
+ * have to be handled properly. XXX This option does not use the
+ * mail_queue(3) API for message queue files, so that it can be
+ * run on a queue that does not have message queue files in the
+ * "right" place. It would be terribly inefficient to first have
+ * to rename files into place before fixing the file names.
+ */
+ if ((action & ACTION_STRUCT) != 0 && MESSAGE_QUEUE(qp)) {
+ if (sscanf(path + 5, "%lx", &inum) != 1) {
+ msg_warn("bogus file name: %s", STR(actual_path));
+ continue;
+ }
+ if (inum != (unsigned long) st.st_ino) {
+ inode_mismatch++; /* before we fix */
+ fix_queue_id(STR(actual_path), queue_name, path, st.st_ino);
+ /* At this point, path and actual_path are invalidated. */
+ continue;
+ }
+ }
+
/*
* See if this file sits in the right place in the file system
* hierarchy. Its place may be wrong after a change to the
- * hash_queue_{names,depth} parameter settings. The implied
- * mkdir() operation is the main reason for this command to run
- * with postfix privilege. The mail_queue_mkdirs() routine could
- * be fixed to use the "right" privilege, but it is a good idea
- * to do everything with the postfix owner privileges regardless,
- * in order to limit the amount of damage that we can do.
+ * hash_queue_{names,depth} parameter settings.
*/
if (action & ACTION_STRUCT) {
(void) mail_queue_path(wanted_path, queue_name, path);
if (strcmp(STR(actual_path), STR(wanted_path)) != 0) {
- if (rename(STR(actual_path), STR(wanted_path)) < 0)
- if (errno != ENOENT
- || mail_queue_mkdirs(STR(wanted_path)) < 0
- || rename(STR(actual_path), STR(wanted_path)) < 0)
- msg_fatal("rename %s to %s: %m", STR(actual_path),
- STR(wanted_path));
- if (msg_verbose)
- msg_info("rename %s to %s", STR(actual_path),
- STR(wanted_path));
+ position_mismatch++; /* before we fix */
+ (void) postrename(STR(actual_path), STR(wanted_path));
+ /* At this point, path and actual_path are invalidated. */
+ continue;
}
}
}
@@ -497,27 +718,43 @@ static void super(char **queues, int action)
argv_free(hash_queue_names);
}
+/* fatal_exit - print warning if queue fix is incomplete */
+
+static void fatal_exit(void)
+{
+ if (inode_mismatch > 0 || inode_fixed > 0 || position_mismatch > 0)
+ msg_fatal("OPERATION INCOMPLETE -- RERUN COMMAND TO FIX THE QUEUE FIRST");
+}
+
+/* interrupted - signal handler */
+
+static void interrupted(int unused_sig)
+{
+ fatal_exit();
+}
+
int main(int argc, char **argv)
{
int fd;
struct stat st;
char *slash;
- int debug_me = 0;
int action = 0;
- char **queues;
+ const char **queues;
int c;
- int found = 0;
+ ARGV *requeue_names = 0;
+ ARGV *delete_names = 0;
+ char **cpp;
/*
* Defaults.
*/
static char *default_queues[] = {
+ MAIL_QUEUE_DEFER, /* before message directories */
+ MAIL_QUEUE_BOUNCE, /* before message directories */
MAIL_QUEUE_MAILDROP,
MAIL_QUEUE_INCOMING,
MAIL_QUEUE_ACTIVE,
MAIL_QUEUE_DEFERRED,
- MAIL_QUEUE_DEFER,
- MAIL_QUEUE_BOUNCE,
MAIL_QUEUE_FLUSH,
0,
};
@@ -544,8 +781,6 @@ int main(int argc, char **argv)
*/
if (safe_getenv(CONF_ENV_VERB))
msg_verbose = 1;
- if (safe_getenv(CONF_ENV_DEBUG))
- debug_me = 1;
/*
* Initialize. Set up logging, read the global configuration file and
@@ -561,6 +796,17 @@ int main(int argc, char **argv)
if (chdir(var_queue_dir))
msg_fatal("chdir %s: %m", var_queue_dir);
+ /*
+ * Be sure to log a warning if we do not finish structural repair. Maybe
+ * we should have an fsck-style "clean" flag so Postfix will not start
+ * with a broken queue.
+ */
+ signal(SIGHUP, interrupted);
+ signal(SIGINT, interrupted);
+ signal(SIGQUIT, interrupted);
+ signal(SIGTERM, interrupted);
+ msg_cleanup(fatal_exit);
+
/*
* All file/directory updates must be done as the mail system owner. This
* is because Postfix daemons manipulate the queue with those same
@@ -579,30 +825,27 @@ int main(int argc, char **argv)
/*
* Parse JCL.
*/
- while ((c = GETOPT(argc, argv, "d:spv")) > 0) {
+ while ((c = GETOPT(argc, argv, "d:pr:sv")) > 0) {
switch (c) {
default:
- msg_fatal("usage: %s [-d queue_id (delete message)] [-p (purge stale files)] [-r queue_id (requeue message)] [-R (requeue all)] [-s (fix structure)]",
+ msg_fatal("usage: %s [-d queue_id (delete)] [-p (purge temporary files)] [-r queue_id (requeue)] [-s (structure fix)]",
argv[0]);
case 'd':
- if (strcmp(optarg, "-") == 0)
- found |= operate_stream(VSTREAM_IN, delete_one);
- else
- found |= delete_one(optarg);
- action |= ACTION_DELETE;
+ if (delete_names == 0)
+ delete_names = argv_alloc(1);
+ argv_add(delete_names, optarg, (char *) 0);
+ action |= (strcmp(optarg, "ALL") == 0 ?
+ ACTION_DELETE_ALL : ACTION_DELETE_ONE);
break;
case 'p':
action |= ACTION_PURGE;
break;
case 'r':
- if (strcmp(optarg, "-") == 0)
- found |= operate_stream(VSTREAM_IN, requeue_one);
- else
- found |= requeue_one(optarg);
- action |= ACTION_REQUEUE;
- break;
- case 'R':
- action |= ACTION_REQUEUE_ALL;
+ if (requeue_names == 0)
+ requeue_names = argv_alloc(1);
+ argv_add(requeue_names, optarg, (char *) 0);
+ action |= (strcmp(optarg, "ALL") == 0 ?
+ ACTION_REQUEUE_ALL : ACTION_REQUEUE_ONE);
break;
case 's':
action |= ACTION_STRUCT;
@@ -616,17 +859,94 @@ int main(int argc, char **argv)
/*
* Execute the explicitly specified (or default) action, on the
* explicitly specified (or default) queues.
+ *
+ * XXX Work around gcc const brain damage.
*/
if (action == 0)
action = ACTION_DEFAULT;
if (argv[optind] == 0)
- queues = default_queues;
+ queues = (const char **) default_queues;
else
- queues = argv + optind;
+ queues = (const char **) argv + optind;
-#define ACTIONS_ON_THE_FLY (ACTION_DELETE | ACTION_REQUEUE)
+#define ACTIONS_BY_QUEUE_ID (ACTION_DELETE_ONE | ACTION_REQUEUE_ONE)
- if (action & ~ACTIONS_ON_THE_FLY)
- super(queues, action & ~ACTIONS_ON_THE_FLY);
- exit((action & ACTIONS_ON_THE_FLY) ? !found : 0);
+ /*
+ * Basic queue maintenance, as well as mass deletion, mass requeuing, and
+ * mass name-to-inode fixing. This ensures that queue files are in the
+ * right place before the queue file by name operations are done.
+ */
+ if (action & ~ACTIONS_BY_QUEUE_ID)
+ super(queues, action & ~ACTIONS_BY_QUEUE_ID);
+ if (inode_mismatch > 0)
+ super(queues, 0);
+
+ /*
+ * Delete queue files by name. This must not be done when queue files
+ * have changed names as a result of the structure check, because we
+ * could be deleiting the wrong message.
+ */
+ if (action & ACTION_DELETE_ONE) {
+ if (inode_mismatch > 0 || inode_fixed > 0) {
+ msg_error("QUEUE FILE NAMES WERE CHANGED TO MATCH INODE NUMBERS");
+ msg_fatal("CHECK YOUR QUEUE IDS AND RE-ISSUE THE COMMAND");
+ }
+ argv_terminate(delete_names);
+ for (cpp = delete_names->argv; *cpp; cpp++) {
+ if (strcmp(*cpp, "ALL") == 0)
+ continue;
+ if (strcmp(*cpp, "-") == 0)
+ message_deleted +=
+ operate_stream(VSTREAM_IN, delete_one, queues);
+ else
+ message_deleted += delete_one(queues, *cpp);
+ }
+ }
+
+ /*
+ * Requeue queue files by name. This must not be done when queue files
+ * have changed names as a result of the structure check, because we
+ * could be requeuing the wrong message.
+ */
+ if (action & ACTION_REQUEUE_ONE) {
+ if (inode_mismatch > 0 || inode_fixed > 0) {
+ msg_error("QUEUE FILE NAMES WERE CHANGED TO MATCH INODE NUMBERS");
+ msg_fatal("CHECK YOUR QUEUE IDS AND RE-ISSUE THE COMMAND");
+ }
+ argv_terminate(requeue_names);
+ for (cpp = requeue_names->argv; *cpp; cpp++) {
+ if (strcmp(*cpp, "ALL") == 0)
+ continue;
+ if (strcmp(*cpp, "-") == 0)
+ message_requeued +=
+ operate_stream(VSTREAM_IN, requeue_one, queues);
+ else
+ message_requeued += requeue_one(queues, *cpp);
+ }
+ }
+
+ /*
+ * Report.
+ */
+ if (message_requeued > 0)
+ msg_info("Requeued: %d message%s", message_requeued,
+ message_requeued > 1 ? "s" : "");
+ if (message_deleted > 0)
+ msg_info("Deleted: %d message%s", message_deleted,
+ message_deleted > 1 ? "s" : "");
+ if (inode_fixed > 0)
+ msg_info("Renamed to match inode number: %d message%s", inode_fixed,
+ inode_fixed > 1 ? "s" : "");
+ if (inode_mismatch > 0 || inode_fixed > 0)
+ msg_warn("QUEUE FILE NAMES WERE CHANGED TO MATCH INODE NUMBERS");
+
+ /*
+ * Clean up.
+ */
+ if (requeue_names)
+ argv_free(requeue_names);
+ if (delete_names)
+ argv_free(delete_names);
+
+ exit(0);
}
diff --git a/postfix/src/util/dict_ldap.c b/postfix/src/util/dict_ldap.c
index 44b121d81..776a437db 100644
--- a/postfix/src/util/dict_ldap.c
+++ b/postfix/src/util/dict_ldap.c
@@ -584,6 +584,8 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
ldap_msgfree(res);
if (filter_buf != 0)
vstring_free(filter_buf);
+ if (escaped_name != 0)
+ vstring_free(escaped_name);
/*
* If we had an error, return nothing, Otherwise, return the result, if