]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
snapshot-20010524
authorWietse Venema <wietse@porcupine.org>
Thu, 24 May 2001 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <viktor@dukhovni.org>
Tue, 5 Feb 2013 06:27:18 +0000 (06:27 +0000)
12 files changed:
postfix/HISTORY
postfix/RELEASE_NOTES
postfix/conf/postfix-script-nosgid
postfix/conf/postfix-script-sgid
postfix/html/faq.html
postfix/html/postsuper.1.html
postfix/man/man1/postsuper.1
postfix/src/global/file_id.c
postfix/src/global/mail_queue.c
postfix/src/global/mail_version.h
postfix/src/postsuper/postsuper.c
postfix/src/util/dict_ldap.c

index ef773ce676212e2e9a4dd0c2f29813be43714ec4..a7f7bb21499d841ec535ac82f96c01875b5891b2 100644 (file)
@@ -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" re-queues all mail. This is useful
-       after restoring a Postfix queue from another machine, or
-       from backup.
+       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.
+
+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.
index 9fc8bffe8d5196b2f6c133bf8a33bf1203f8faa8..3a7c71adb81b663f3b91e1baad2a5d98a72ee5b7 100644 (file)
@@ -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
 ====================================
 
index f845366758eeb0ecb32b03105fc9bc7f81090a17..7297df1e9f8ca414ac25cf00b397191c46c5d9b3 100755 (executable)
@@ -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: {} \;
 
index 53bcf6303733f13dad4f04e204f36706e5ac545d..98207c5e83161ae31cff61a2e10b36b3afe94aa4 100755 (executable)
@@ -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: {} \;
 
index 47f4d9ec53056dabfa190c4041ea89f378f3d6ec..011e7528ec8dd411f90c15a805590ac577bbece2 100644 (file)
@@ -3063,13 +3063,61 @@ that the file name will collide with another queue file.
 
 <p>
 
-To avoid queue file name collisions when copying queue files,
-restore the incoming, active and deferred queue files under the
-maildrop directory instead.
+<ul>
+
+<li>If your Postfix queue is empty, and if you run a Postfix release
+after 20010524, you can use this procedure to copy a Postfix
+queue from another machine or to restore it from backup.
+
+<p>
+
+<ul>
+
+<li> Stop Postfix, if it was running.
+
+<p>
+
+<li> Execute the <b>mailq</b> command. If there is any output, do
+not complete this procedure, but use the second procedure instead.
 
 <p>
 
-As of late 2000, Postfix queues are all hashed (for example, file
+<li> Copy or restore the queue to the usual place.
+
+<p>
+
+<li> Run the <b>postsuper</b> command. This command will rename
+queue files so that the name matches the message file inode number.
+
+</ul>
+
+<p>
+
+<li> If your Postfix queue is not empty, or if you are running a
+Postfix release prior to 20010524, use the following procedure
+instead.
+
+<p>
+
+<ul>
+
+<li>Stop Postfix, if it was running.
+
+<p>
+
+<li> To avoid queue file name collisions when restoring queue files,
+copy or restore the incoming, active and deferred queue files under
+the maildrop directory instead.
+
+<p>
+
+<li>While the next step is going on, don't submit new mail locally,
+because that could collide with the files you are restoring under
+the maildrop directory.
+
+<p>
+
+<li>As of late 2000, Postfix queues are all hashed (for example, file
 ABCDEF is stored as A/B/ABCDEF), so you need an additional step to
 move files down from their subdirectories.
 
@@ -3083,15 +3131,15 @@ move files down from their subdirectories.
     # postfix start
 </pre>
 
-While all this is going on, don't submit new mail locally, because
-that could collide with the files you are restoring under the
-maildrop directory.
-
 <p>
 
-When Postfix is started, it will pick up queue files from the
+<li>When Postfix is started, it will pick up queue files from the
 maildrop directory and will give them proper queue file names.
 
+</ul>
+
+</ul>
+
 <hr>
 
 <a name="bind"><h3>Undefined symbols: ___dn_expand, ___res_init etc.</h3></a>
index c608c6e0129c1407ecca5f45c436edf98d73af8a..724c28214e2c17243b6e359f1a0c330b6d0848b6 100644 (file)
@@ -6,59 +6,59 @@ POSTSUPER(1)                                         POSTSUPER(1)
 
 
 <b>NAME</b>
-       postsuper - Postfix super intendent
+       postsuper - Postfix superintendent
 
 <b>SYNOPSIS</b>
        <b>postsuper</b> [<b>-psv</b>] [<b>-d</b> <i>queue_id</i>] [<b>-r</b> <i>queue_id</i>] [<i>directory</i>
        <i>...</i>]
 
 <b>DESCRIPTION</b>
-       The <b>postsuper</b> command does small maintenance jobs. Use  of
-       the command is restricted to the super-user.
+       The <b>postsuper</b> command does maintenance jobs on the Postfix
+       queue. Use of the command is restricted to the super-user.
 
-       By  default,  <b>postsuper</b>  performs the operations requested
-       with the <b>-s</b> and <b>-p</b> 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, <b>postsuper</b> performs  the  operations  requested
+       with  the  <b>-s</b>  and  <b>-p</b> command-line options on all Postfix
+       queue directories - this includes the <b>incoming</b>, <b>active</b> and
+       <b>deferred</b> directories with mail files and the <b>bounce</b>, <b>defer</b>
+       and <b>flush</b> directories with log files.
 
        Options:
 
-       <b>-d</b>     This  option  ignores  any  <i>directory</i>  argument(s).
-              Delete  one message queue file with the named queue
-              ID.  Specify multiple <b>-d</b> options to delete multiple
-              queue files by name.
-
-              Alternatively, if a <i>queue_id</i> of <b>-</b> is specified, the
+       <b>-d</b> <i>queue_id</i>
+              Delete one message with the named queue ID from the
+              named  mail queue(s) (default: <b>incoming</b>, <b>active</b> and
+              <b>deferred</b>).  If a <i>queue_id</i> of <b>-</b>  is  specified,  the
               program reads queue IDs from standard input.
 
-              The <b>postsuper</b> exit status is non-zero when no  mes-
-              sage queue file was deleted.
+              Specify <b>-d</b> <b>ALL</b> to remove all messages; for example,
+              specify <b>-d</b> <b>ALL</b>  <b>deferred</b>  to  delete  mail  in  the
+              <b>deferred</b>  queue.  As a safety measure, the word <b>ALL</b>
+              must be specified in upper case.
 
-              <b>There</b>  <b>is</b>  <b>a</b>  <b>very</b> <b>small</b> <b>possibility</b> <b>that</b> <b>postsuper</b>
-              <b>deletes</b> <b>the</b> <b>wrong</b> <b>message</b> <b>file</b> <b>when</b> <b>it</b> <b>is</b>  <b>executed</b>
-              <b>while</b> <b>the</b> <b>Postfix</b> <b>mail</b> <b>system</b> <b>is</b> <b>running.</b>
+              <b>Postfix</b> <b>queue</b> <b>IDs</b> <b>are</b>  <b>reused.</b>   <b>There</b>  <b>is</b>  <b>a</b>  <b>very</b>
+              <b>small</b>  <b>possibility</b> <b>that</b> <b>postsuper</b> <b>deletes</b> <b>the</b> <b>wrong</b>
+              <b>message</b> <b>file</b> <b>when</b> <b>it</b> <b>is</b> <b>executed</b> <b>while</b> <b>the</b>  <b>Postfix</b>
+              <b>mail</b> <b>system</b> <b>is</b> <b>running.</b>
 
               The scenario is as follows:
 
-              <b>o</b>      The  Postfix  queue manager deletes the file
-                     that  <b>postsuper</b>  was  supposed  to   delete,
-                     because  Postfix  was finished with the mes-
+              1)     The  Postfix  queue manager deletes the mes-
+                     sage that <b>postsuper</b> is supposed  to  delete,
+                     because  Postfix  is  finished with the mes-
                      sage.
 
-              <b>o</b>      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
-                     <b>postsuper</b> was supposed to delete.  The prob-
-                     ability  for  reusing  a deleted queue ID is
+                     <b>postsuper</b> 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).
 
-              <b>o</b>      <b>postsuper</b>  deletes  the  new  message  file,
-                     instead  of  the  old  file that should have
-                     been deleted.
+              3)     <b>postsuper</b> deletes the new  message,  instead
+                     of  the  old  message  that  it  should have
+                     deleted.
 
-       <b>-r</b>     This  option  ignores  any  <i>directory</i>  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 <b>-r</b> options to requeue  multi-
-              ple queue files by name.
-
-              Alternatively, if a <i>queue_id</i> of <b>-</b> is specified, the
-              program 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.
-
-              The <b>postsuper</b> exit status is non-zero when no  mes-
-              sage queue file was requeued.
-
-       <b>-s</b>     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
-              <b>hash</b><i>_</i><b>queue</b><i>_</i><b>names</b> and/or <b>hash</b><i>_</i><b>queue</b><i>_</i><b>depth</b> configura-
-              tion parameters. It is highly  recommended  to  run
-              this check once before Postfix startup.
-
-       <b>-p</b>     Purge  stale  files (files that are left over after
-              system or software crashes).
+       <b>-p</b>     Purge old temporary files that are left over  after
+              system or software crashes.
+
+       <b>-r</b> <i>queue_id</i>
+              Requeue  the  message  with the named queue ID from
+              the named mail queue(s) (default: <b>incoming</b>,  <b>active</b>
+              and <b>deferred</b>).  To requeue multiple messages, spec-
+              ify multiple  <b>-r</b>  command-line  options.   Alterna-
+              tively,  if  a <i>queue_id</i> of <b>-</b> is specified, the pro-
+              gram reads queue IDs from standard input.
+
+              Specify <b>-r</b> <b>ALL</b> to requeue all messages. As a safety
+              measure,  the  word  <b>ALL</b> must be specified in upper
+              case.
+
+              A requeued message is moved to the <b>maildrop</b>  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.
+
+              Postfix queue IDs are  reused.   There  is  a  very
+              small possibility that <b>postsuper</b> requeues the wrong
+              message file when it is executed while the  Postfix
+              mail system is running, but no harm should be done.
+
+       <b>-s</b>     Structure check and structure repair.  It is highly
+              recommended  to  perform this operation once before
+              Postfix startup.
+
+              <b>o</b>      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.
+
+              <b>o</b>      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  <b>hash</b><i>_</i><b>queue</b><i>_</i><b>names</b>  and/or
+                     <b>hash</b><i>_</i><b>queue</b><i>_</i><b>depth</b> configuration parameters.
 
        <b>-v</b>     Enable verbose logging for debugging purposes. Mul-
               tiple  <b>-v</b>  options  make  the software increasingly
@@ -107,6 +123,24 @@ POSTSUPER(1)                                         POSTSUPER(1)
        Problems are reported to the standard error stream and  to
        <b>syslogd</b>.
 
+       <b>postsuper</b>  reports the number of messages deleted with <b>-d</b>,
+       the number of messages requeued with <b>-r</b>, and the number of
+
+
+
+                                                                2
+
+
+
+
+
+POSTSUPER(1)                                         POSTSUPER(1)
+
+
+       messages  whose  queue  file  name  was fixed with <b>-s</b>. The
+       report is written to the standard error stream and to <b>sys-</b>
+       <b>logd</b>.
+
 <b>CONFIGURATION</b> <b>PARAMETERS</b>
        See  the  Postfix  <b>main.cf</b> file for syntax details and for
        default values.
@@ -125,18 +159,6 @@ POSTSUPER(1)                                         POSTSUPER(1)
 <b>AUTHOR(S)</b>
        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)
 
 
 
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
 
 
 
index 4b5e83b43c78f6256c500bb983104aaa8cf6c8d6..80fc638644ad3c0a25e7db7645fcd03316597f35 100644 (file)
@@ -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.
+.IP \fB-p\fR
+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
-Alternatively, if a \fIqueue_id\fR of \fB-\fR is specified, the
-program reads queue IDs from standard input.
+Specify \fB-r ALL\fR to requeue all messages. As a safety
+measure, the word \fBALL\fR must be specified in upper case.
 .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.
+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
-The \fBpostsuper\fR exit status is non-zero when no message queue
-file was requeued.
+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.  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).
+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
index ad4ce57987fea1eb047df98a97e03b143e2f7a4b..e130087db2467e8b0634a5f95ebaff60d80aa688 100644 (file)
@@ -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));
 }
 
index a8c3673c39aec3bff004c2ef89fcb901ad5d0274..9e8e6432380b8ba5247b3749e5e3209b29f9fa8c 100644 (file)
@@ -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. */
index 7e53c391a3ce73fe0ea5e5d796f2acf009419ab8..a667503e82bd86d576406a186999ec0648403d7f 100644 (file)
@@ -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
index da6ab3fd982fde433ef8aa439b936166a35bf5fa..80ee5c30c2100efe4492d933ddf6e71a1031e9d5 100644 (file)
@@ -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.
 /* .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.
+/* .IP \fB-p\fR
+/*     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
-/*     Alternatively, if a \fIqueue_id\fR of \fB-\fR is specified, the
-/*     program reads queue IDs from standard input.
+/*     Specify \fB-r ALL\fR to requeue all messages. As a safety
+/*     measure, the word \fBALL\fR must be specified in upper case.
 /* .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
+/*     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
-/*     The \fBpostsuper\fR exit status is non-zero when no message queue
-/*     file was requeued.
+/*     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.  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).
+/*     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
 #include <stdlib.h>
 #include <errno.h>
 #include <string.h>
+#include <signal.h>
 #include <stdio.h>                     /* remove() */
 
 /* Utility library. */
 
 #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 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(char **queues, int action)
+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));
+               if (rmdir(STR(actual_path)) < 0) {
+                   if (errno != ENOENT)
+                       msg_warn("remove subdirectory %s: %m", STR(actual_path));
+               } else {
+                   if (msg_verbose)
+                       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 &&
-                   (st.st_mode & S_IRWXU) != qp->perms
+               || ((action & ACTION_PURGE) != 0
+                   && MESSAGE_QUEUE(qp)
+                   && !READY_MESSAGE(st)
                    && 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));
+               (void) postremove(STR(actual_path));
+               /* No further work on this object is possible. */
                continue;
            }
 
            /*
-            * Skip temporary files that aren't old enough.
+            * Skip temporary message files that aren't old enough.
             */
-           if (mail_queue_id_ok(path) == 0)
+           if (MESSAGE_QUEUE(qp) && !READY_MESSAGE(st))
+               /* No further work on this object is possible. */
                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.()
+            * 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 (rename(STR(actual_path), STR(wanted_path)) < 0) {
-                   if (errno != ENOENT)
-                       msg_fatal("rename %s to %s: %m", STR(actual_path),
-                                 STR(wanted_path));
-               } else {
-                   if (msg_verbose)
-                       msg_info("rename %s to %s", STR(actual_path),
-                                STR(wanted_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_BY_QUEUE_ID (ACTION_DELETE_ONE | ACTION_REQUEUE_ONE)
+
+    /*
+     * 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);
 
-#define ACTIONS_ON_THE_FLY (ACTION_DELETE | ACTION_REQUEUE)
+    /*
+     * 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);
 
-    if (action & ~ACTIONS_ON_THE_FLY)
-       super(queues, action & ~ACTIONS_ON_THE_FLY);
-    exit((action & ACTIONS_ON_THE_FLY) ? !found : 0);
+    exit(0);
 }
index 44b121d81271e0e51487e727e133e354a836613e..776a437db560b9e6c03c48a99f2b13f9250227ab 100644 (file)
@@ -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