Merge from vendor branch OPENSSH:
[dragonfly.git] / contrib / sendmail-8.13.4 / contrib / re-mqueue.pl
1 #!/usr/bin/perl
2 #
3 # re-mqueue -- requeue messages from queueA to queueB based on age.
4 #
5 #       Contributed by Paul Pomes <ppomes@Qualcomm.COM>.
6 #               http://www.qualcomm.com/~ppomes/
7 #
8 # Usage: re-mqueue [-d] queueA queueB seconds
9 #
10 #  -d           enable debugging
11 #  queueA       source directory
12 #  queueB       destination directory
13 #  seconds      select files older than this number of seconds 
14 #
15 # Example: re-mqueue /var/spool/mqueue /var/spool/mqueue2 2700
16 #
17 # Moves the qf* and df* files for a message from /var/spool/mqueue to
18 # /var/spool/mqueue2 if the df* file is over 2700 seconds old.
19 #
20 # The qf* file can't be used for age checking as it's partially re-written
21 # with the results of the last queue run.
22 #
23 # Rationale: With a limited number of sendmail processes allowed to run,
24 # messages that can't be delivered immediately slow down the ones that can.
25 # This becomes especially important when messages are being queued instead
26 # of delivered right away, or when the queue becomes excessively deep.
27 # By putting messages that have already failed one or more delivery attempts
28 # into another queue, the primary queue can be kept small and fast.
29 #
30 # On postoffice.cso.uiuc.edu, the primary sendmail daemon runs the queue
31 # every thirty minutes.  Messages over 45 minutues old are moved to
32 # /var/spool/mqueue2 where sendmail runs every hour.  Messages more than
33 # 3.25 hours old are moved to /var/spool/mqueue3 where sendmail runs every
34 # four hours.  Messages more than a day old are moved to /var/spool/mqueue4
35 # where sendmail runs three times a day.  The idea is that a message is
36 # tried at least twice in the first three queues before being moved to the
37 # old-age ghetto.
38 #
39 # (Each must be re-formed into a single line before using in crontab)
40 #
41 # 08 * * * *    /usr/local/libexec/re-mqueue /var/spool/mqueue ##                                               /var/spool/mqueue2 2700
42 # 11 * * * *    /usr/lib/sendmail -oQ/var/spool/mqueue2 -q > ##                                                 > /var/log/mqueue2 2>&1
43 # 38 * * * *    /usr/local/libexec/re-mqueue /var/spool/mqueue2
44 #                                       /var/spool/mqueue3 11700
45 # 41 1,5,9,13,17,21 * * * /usr/lib/sendmail -oQ/var/spool/mqueue3 -q ##                                                 > /var/log/mqueue3 2>&1
46 # 48 * * * *    /usr/local/libexec/re-mqueue /var/spool/mqueue3
47 #                                       /var/spool/mqueue4 100000
48 #53 3,11,19 * * * /usr/lib/sendmail -oQ/var/spool/mqueue4 -q > ##                                                       > /var/log/mqueue4 2>&1
49 #
50 #
51 # N.B., the moves are done with link().  This has two effects: 1) the mqueue*
52 # directories must all be on the same filesystem, and 2) the file modification
53 # times are not changed.  All times must be cumulative from when the df*
54 # file was created.
55 #
56 # Copyright (c) 1995 University of Illinois Board of Trustees and Paul Pomes
57 # All rights reserved.
58 #
59 # Redistribution and use in source and binary forms, with or without
60 # modification, are permitted provided that the following conditions
61 # are met:
62 # 1. Redistributions of source code must retain the above copyright
63 #    notice, this list of conditions and the following disclaimer.
64 # 2. Redistributions in binary form must reproduce the above copyright
65 #    notice, this list of conditions and the following disclaimer in the
66 #    documentation and/or other materials provided with the distribution.
67 # 3. All advertising materials mentioning features or use of this software
68 #    must display the following acknowledgement:
69 #       This product includes software developed by the University of
70 #       Illinois at Urbana and their contributors.
71 # 4. Neither the name of the University nor the names of their contributors
72 #    may be used to endorse or promote products derived from this software
73 #    without specific prior written permission.
74 #
75 # THIS SOFTWARE IS PROVIDED BY THE TRUSTEES AND CONTRIBUTORS ``AS IS'' AND
76 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
77 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
78 # ARE DISCLAIMED.  IN NO EVENT SHALL THE TRUSTEES OR CONTRIBUTORS BE LIABLE
79 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
80 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
81 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
82 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
83 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
84 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
85 # SUCH DAMAGE.
86 #
87 # @(#)$OrigId: re-mqueue,v 1.3 1995/05/25 18:14:53 p-pomes Exp $
88 #
89 # Updated by Graeme Hewson <ghewson@uk.oracle.com> May 1999
90 #
91 #       'use Sys::Syslog' for Perl 5
92 #       Move transcript (xf) files if they exist
93 #       Allow zero-length df files (empty message body)
94 #       Preserve $! for error messages
95 #
96 # Updated by Graeme Hewson <ghewson@uk.oracle.com> April 2000
97 #
98 #       Improve handling of race between re-mqueue and sendmail
99 #
100 # Updated by Graeme Hewson <graeme.hewson@oracle.com> June 2000
101 #
102 #       Don't exit(0) at end so can be called as subroutine
103 #
104 # NB This program can't handle separate qf/df/xf subdirectories
105 # as introduced in sendmail 8.10.0.
106 #
107
108 use Sys::Syslog;
109
110 $LOCK_EX = 2;
111 $LOCK_NB = 4;
112 $LOCK_UN = 8;
113
114 # Count arguments, exit if wrong in any way.
115 die "Usage: $0 [-d] queueA queueB seconds\n" if ($#ARGV < 2);
116
117 while ($_ = $ARGV[0], /^-/) {
118     shift;
119     last if /^--$/;
120     /^-d/ && $debug++;
121 }
122
123 $queueA = shift;
124 $queueB = shift;
125 $age = shift;
126
127 die "$0: $queueA not a directory\n" if (! -d $queueA);
128 die "$0: $queueB not a directory\n" if (! -d $queueB);
129 die "$0: $age isn't a valid number of seconds for age\n" if ($age =~ /\D/);
130
131 # chdir to $queueA and read the directory.  When a df* file is found, stat it.
132 # If it's older than $age, lock the corresponding qf* file.  If the lock
133 # fails, give up and move on.  Once the lock is obtained, verify that files
134 # of the same name *don't* already exist in $queueB and move on if they do.
135 # Otherwise re-link the qf* and df* files into $queueB then release the lock.
136
137 chdir "$queueA" || die "$0: can't cd to $queueA: $!\n";
138 opendir (QA, ".") || die "$0: can't open directory $queueA for reading: $!\n";
139 @dfiles = grep(/^df/, readdir(QA));
140 $now = time();
141 ($program = $0) =~ s,.*/,,;
142 &openlog($program, 'pid', 'mail');
143
144 # Loop through the dfiles
145 while ($dfile = pop(@dfiles)) {
146     print "Checking $dfile\n" if ($debug);
147     ($qfile = $dfile) =~ s/^d/q/;
148     ($xfile = $dfile) =~ s/^d/x/;
149     ($mfile = $dfile) =~ s/^df//;
150     if (! -e $qfile || -z $qfile) {
151         print "$qfile is gone or zero bytes - skipping\n" if ($debug);
152         next;
153     }
154
155     ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
156      $atime,$mtime,$ctime,$blksize,$blocks) = stat($dfile);
157     if (! defined $mtime) {
158         print "$dfile is gone - skipping\n" if ($debug);
159         next;
160     }
161
162     # Compare timestamps
163     if (($mtime + $age) > $now) {
164         printf ("%s is %d seconds old - skipping\n", $dfile, $now-$mtime) if ($debug);
165         next;
166     }
167
168     # See if files of the same name already exist in $queueB
169     if (-e "$queueB/$dfile") {
170         print "$queueb/$dfile already exists - skipping\n" if ($debug);
171         next;
172     }
173     if (-e "$queueB/$qfile") {
174         print "$queueb/$qfile already exists - skipping\n" if ($debug);
175         next;
176     }
177     if (-e "$queueB/$xfile") {
178         print "$queueb/$xfile already exists - skipping\n" if ($debug);
179         next;
180     }
181
182     # Try and lock qf* file
183     unless (open(QF, ">>$qfile")) {
184         print "$qfile: $!\n" if ($debug);
185         next;
186     }
187     $retval = flock(QF, $LOCK_EX|$LOCK_NB) || ($retval = -1);
188     if ($retval == -1) {
189         print "$qfile already flock()ed - skipping\n" if ($debug);
190         close(QF);
191         next;
192     }
193     print "$qfile now flock()ed\n" if ($debug);
194
195     # Check df* file again in case sendmail got in
196     if (! -e $dfile) {
197         print "$mfile sent - skipping\n" if ($debug);
198         # qf* file created by ourselves at open? (Almost certainly)
199         if (-z $qfile) {
200            unlink($qfile);
201         }
202         close(QF);
203         next;
204     }
205
206     # Show time!  Do the link()s
207     if (link("$dfile", "$queueB/$dfile") == 0) {
208         $bang = $!;
209         &syslog('err', 'link(%s, %s/%s): %s', $dfile, $queueB, $dfile, $bang);
210         print STDERR "$0: link($dfile, $queueB/$dfile): $bang\n";
211         exit (1);
212     }
213     if (link("$qfile", "$queueB/$qfile") == 0) {
214         $bang = $!;
215         &syslog('err', 'link(%s, %s/%s): %s', $qfile, $queueB, $qfile, $bang);
216         print STDERR "$0: link($qfile, $queueB/$qfile): $bang\n";
217         unlink("$queueB/$dfile");
218         exit (1);
219     }
220     if (-e "$xfile") {
221         if (link("$xfile", "$queueB/$xfile") == 0) {
222             $bang = $!;
223             &syslog('err', 'link(%s, %s/%s): %s', $xfile, $queueB, $xfile, $bang);
224             print STDERR "$0: link($xfile, $queueB/$xfile): $bang\n";
225             unlink("$queueB/$dfile");
226             unlink("$queueB/$qfile");
227             exit (1);
228         }
229     }
230
231     # Links created successfully.  Unlink the original files, release the
232     # lock, and close the file.
233     print "links ok\n" if ($debug);
234     if (unlink($qfile) == 0) {
235         $bang = $!;
236         &syslog('err', 'unlink(%s): %s', $qfile, $bang);
237         print STDERR "$0: unlink($qfile): $bang\n";
238         exit (1);
239     }
240     if (unlink($dfile) == 0) {
241         $bang = $!;
242         &syslog('err', 'unlink(%s): %s', $dfile, $bang);
243         print STDERR "$0: unlink($dfile): $bang\n";
244         exit (1);
245     }
246     if (-e "$xfile") {
247         if (unlink($xfile) == 0) {
248             $bang = $!;
249             &syslog('err', 'unlink(%s): %s', $xfile, $bang);
250             print STDERR "$0: unlink($xfile): $bang\n";
251             exit (1);
252         }
253     }
254     flock(QF, $LOCK_UN);
255     close(QF);
256     &syslog('info', '%s moved to %s', $mfile, $queueB);
257     print "Done with $dfile $qfile\n\n" if ($debug);
258 }