re-mqueue.pl revision 38032
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# @(#)$Id: re-mqueue,v 1.3 1995/05/25 18:14:53 p-pomes Exp $ 88 89require "syslog.pl"; 90 91$LOCK_EX = 2; 92$LOCK_NB = 4; 93$LOCK_UN = 8; 94 95# Count arguments, exit if wrong in any way. 96die "Usage: $0 [-d] queueA queueB seconds\n" if ($#ARGV < 2); 97 98while ($_ = $ARGV[0], /^-/) { 99 shift; 100 last if /^--$/; 101 /^-d/ && $debug++; 102} 103 104$queueA = shift; 105$queueB = shift; 106$age = shift; 107 108die "$0: $queueA not a directory\n" if (! -d $queueA); 109die "$0: $queueB not a directory\n" if (! -d $queueB); 110die "$0: $age isn't a valid number of seconds for age\n" if ($age =~ /\D/); 111 112# chdir to $queueA and read the directory. When a df* file is found, stat it. 113# If it's older than $age, lock the corresponding qf* file. If the lock 114# fails, give up and move on. Once the lock is obtained, verify that files 115# of the same name *don't* already exist in $queueB and move on if they do. 116# Otherwise re-link the qf* and df* files into $queueB then release the lock. 117 118chdir "$queueA" || die "$0: can't cd to $queueA: $!\n"; 119opendir (QA, ".") || die "$0: can't open directory $queueA for reading: $!\n"; 120@dfiles = grep(/^df/, readdir(QA)); 121$now = time(); 122($program = $0) =~ s,.*/,,; 123&openlog($program, 'pid', 'mail'); 124 125# Loop through the dfiles 126while ($dfile = pop(@dfiles)) { 127 print "Checking $dfile\n" if ($debug); 128 ($qfile = $dfile) =~ s/^d/q/; 129 ($mfile = $dfile) =~ s/^df//; 130 if (! -e $dfile || -z $dfile) { 131 print "$dfile is gone or zero bytes - skipping\n" if ($debug); 132 next; 133 } 134 if (! -e $qfile || -z $qfile) { 135 print "$qfile is gone or zero bytes - skipping\n" if ($debug); 136 next; 137 } 138 139 $mtime = $now; 140 ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, 141 $atime,$mtime,$ctime,$blksize,$blocks) = stat($dfile); 142 143 # Compare timestamps 144 if (($mtime + $age) > $now) { 145 printf ("%s is %d seconds old - skipping\n", $dfile, $now-$mtime) if ($debug); 146 next; 147 } 148 149 # See if files of the same name already exist in $queueB 150 if (-e "$queueB/$dfile") { 151 print "$queueb/$dfile already exists - skipping\n" if ($debug); 152 next; 153 } 154 if (-e "$queueB/$qfile") { 155 print "$queueb/$qfile already exists - skipping\n" if ($debug); 156 next; 157 } 158 159 # Try and lock qf* file 160 unless (open(QF, ">>$qfile")) { 161 print "$qfile: $!\n" if ($debug); 162 next; 163 } 164 $retval = flock(QF, $LOCK_EX|$LOCK_NB) || ($retval = -1); 165 if ($retval == -1) { 166 print "$qfile already flock()ed - skipping\n" if ($debug); 167 close(QF); 168 next; 169 } 170 print "$qfile now flock()ed\n" if ($debug); 171 172 # Show time! Do the link()s 173 if (link("$dfile", "$queueB/$dfile") == 0) { 174 &syslog('err', 'link(%s, %s/%s): %m', $dfile, $queueB, $dfile); 175 print STDERR "$0: link($dfile, $queueB/$dfile): $!\n"; 176 exit (1); 177 } 178 if (link("$qfile", "$queueB/$qfile") == 0) { 179 &syslog('err', 'link(%s, %s/%s): %m', $qfile, $queueB, $qfile); 180 print STDERR "$0: link($qfile, $queueB/$qfile): $!\n"; 181 unlink("$queueB/$dfile"); 182 exit (1); 183 } 184 185 # Links created successfully. Unlink the original files, release the 186 # lock, and close the file. 187 print "links ok\n" if ($debug); 188 if (unlink($qfile) == 0) { 189 &syslog('err', 'unlink(%s): %m', $qfile); 190 print STDERR "$0: unlink($qfile): $!\n"; 191 exit (1); 192 } 193 if (unlink($dfile) == 0) { 194 &syslog('err', 'unlink(%s): %m', $dfile); 195 print STDERR "$0: unlink($dfile): $!\n"; 196 exit (1); 197 } 198 flock(QF, $LOCK_UN); 199 close(QF); 200 &syslog('info', '%s moved to %s', $mfile, $queueB); 201 print "Done with $dfile $qfile\n\n" if ($debug); 202} 203exit 0; 204