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