138032Speter#!/usr/bin/perl 238032Speter# 338032Speter# re-mqueue -- requeue messages from queueA to queueB based on age. 438032Speter# 538032Speter# Contributed by Paul Pomes <ppomes@Qualcomm.COM>. 638032Speter# http://www.qualcomm.com/~ppomes/ 738032Speter# 838032Speter# Usage: re-mqueue [-d] queueA queueB seconds 938032Speter# 1038032Speter# -d enable debugging 1138032Speter# queueA source directory 1238032Speter# queueB destination directory 1338032Speter# seconds select files older than this number of seconds 1438032Speter# 1538032Speter# Example: re-mqueue /var/spool/mqueue /var/spool/mqueue2 2700 1638032Speter# 1738032Speter# Moves the qf* and df* files for a message from /var/spool/mqueue to 1838032Speter# /var/spool/mqueue2 if the df* file is over 2700 seconds old. 1938032Speter# 2038032Speter# The qf* file can't be used for age checking as it's partially re-written 2138032Speter# with the results of the last queue run. 2238032Speter# 2338032Speter# Rationale: With a limited number of sendmail processes allowed to run, 2438032Speter# messages that can't be delivered immediately slow down the ones that can. 2538032Speter# This becomes especially important when messages are being queued instead 2638032Speter# of delivered right away, or when the queue becomes excessively deep. 2738032Speter# By putting messages that have already failed one or more delivery attempts 2838032Speter# into another queue, the primary queue can be kept small and fast. 2938032Speter# 3038032Speter# On postoffice.cso.uiuc.edu, the primary sendmail daemon runs the queue 3138032Speter# every thirty minutes. Messages over 45 minutues old are moved to 3238032Speter# /var/spool/mqueue2 where sendmail runs every hour. Messages more than 3338032Speter# 3.25 hours old are moved to /var/spool/mqueue3 where sendmail runs every 3438032Speter# four hours. Messages more than a day old are moved to /var/spool/mqueue4 3538032Speter# where sendmail runs three times a day. The idea is that a message is 3638032Speter# tried at least twice in the first three queues before being moved to the 3738032Speter# old-age ghetto. 3838032Speter# 3938032Speter# (Each must be re-formed into a single line before using in crontab) 4038032Speter# 4138032Speter# 08 * * * * /usr/local/libexec/re-mqueue /var/spool/mqueue ## /var/spool/mqueue2 2700 4238032Speter# 11 * * * * /usr/lib/sendmail -oQ/var/spool/mqueue2 -q > ## > /var/log/mqueue2 2>&1 4338032Speter# 38 * * * * /usr/local/libexec/re-mqueue /var/spool/mqueue2 4438032Speter# /var/spool/mqueue3 11700 4538032Speter# 41 1,5,9,13,17,21 * * * /usr/lib/sendmail -oQ/var/spool/mqueue3 -q ## > /var/log/mqueue3 2>&1 4638032Speter# 48 * * * * /usr/local/libexec/re-mqueue /var/spool/mqueue3 4738032Speter# /var/spool/mqueue4 100000 4838032Speter#53 3,11,19 * * * /usr/lib/sendmail -oQ/var/spool/mqueue4 -q > ## > /var/log/mqueue4 2>&1 4938032Speter# 5038032Speter# 5138032Speter# N.B., the moves are done with link(). This has two effects: 1) the mqueue* 5238032Speter# directories must all be on the same filesystem, and 2) the file modification 5338032Speter# times are not changed. All times must be cumulative from when the df* 5438032Speter# file was created. 5538032Speter# 5638032Speter# Copyright (c) 1995 University of Illinois Board of Trustees and Paul Pomes 5738032Speter# All rights reserved. 5838032Speter# 5938032Speter# Redistribution and use in source and binary forms, with or without 6038032Speter# modification, are permitted provided that the following conditions 6138032Speter# are met: 6238032Speter# 1. Redistributions of source code must retain the above copyright 6338032Speter# notice, this list of conditions and the following disclaimer. 6438032Speter# 2. Redistributions in binary form must reproduce the above copyright 6538032Speter# notice, this list of conditions and the following disclaimer in the 6638032Speter# documentation and/or other materials provided with the distribution. 6738032Speter# 3. All advertising materials mentioning features or use of this software 6838032Speter# must display the following acknowledgement: 6938032Speter# This product includes software developed by the University of 7038032Speter# Illinois at Urbana and their contributors. 7138032Speter# 4. Neither the name of the University nor the names of their contributors 7238032Speter# may be used to endorse or promote products derived from this software 7338032Speter# without specific prior written permission. 7438032Speter# 7538032Speter# THIS SOFTWARE IS PROVIDED BY THE TRUSTEES AND CONTRIBUTORS ``AS IS'' AND 7638032Speter# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 7738032Speter# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 7838032Speter# ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR CONTRIBUTORS BE LIABLE 7938032Speter# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 8038032Speter# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 8138032Speter# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 8238032Speter# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 8338032Speter# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 8438032Speter# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 8538032Speter# SUCH DAMAGE. 8638032Speter# 8764562Sgshapiro# @(#)$OrigId: re-mqueue,v 1.3 1995/05/25 18:14:53 p-pomes Exp $ 8864562Sgshapiro# 8964562Sgshapiro# Updated by Graeme Hewson <ghewson@uk.oracle.com> May 1999 9064562Sgshapiro# 9164562Sgshapiro# 'use Sys::Syslog' for Perl 5 9264562Sgshapiro# Move transcript (xf) files if they exist 9364562Sgshapiro# Allow zero-length df files (empty message body) 9464562Sgshapiro# Preserve $! for error messages 9564562Sgshapiro# 9664562Sgshapiro# Updated by Graeme Hewson <ghewson@uk.oracle.com> April 2000 9764562Sgshapiro# 9864562Sgshapiro# Improve handling of race between re-mqueue and sendmail 9964562Sgshapiro# 10064562Sgshapiro# Updated by Graeme Hewson <graeme.hewson@oracle.com> June 2000 10164562Sgshapiro# 10264562Sgshapiro# Don't exit(0) at end so can be called as subroutine 10364562Sgshapiro# 10464562Sgshapiro# NB This program can't handle separate qf/df/xf subdirectories 10564562Sgshapiro# as introduced in sendmail 8.10.0. 10664562Sgshapiro# 10738032Speter 10864562Sgshapirouse Sys::Syslog; 10938032Speter 11038032Speter$LOCK_EX = 2; 11138032Speter$LOCK_NB = 4; 11238032Speter$LOCK_UN = 8; 11338032Speter 11438032Speter# Count arguments, exit if wrong in any way. 11538032Speterdie "Usage: $0 [-d] queueA queueB seconds\n" if ($#ARGV < 2); 11638032Speter 11738032Speterwhile ($_ = $ARGV[0], /^-/) { 11838032Speter shift; 11938032Speter last if /^--$/; 12038032Speter /^-d/ && $debug++; 12138032Speter} 12238032Speter 12338032Speter$queueA = shift; 12438032Speter$queueB = shift; 12538032Speter$age = shift; 12638032Speter 12738032Speterdie "$0: $queueA not a directory\n" if (! -d $queueA); 12838032Speterdie "$0: $queueB not a directory\n" if (! -d $queueB); 12938032Speterdie "$0: $age isn't a valid number of seconds for age\n" if ($age =~ /\D/); 13038032Speter 13138032Speter# chdir to $queueA and read the directory. When a df* file is found, stat it. 13238032Speter# If it's older than $age, lock the corresponding qf* file. If the lock 13338032Speter# fails, give up and move on. Once the lock is obtained, verify that files 13438032Speter# of the same name *don't* already exist in $queueB and move on if they do. 13538032Speter# Otherwise re-link the qf* and df* files into $queueB then release the lock. 13638032Speter 13738032Speterchdir "$queueA" || die "$0: can't cd to $queueA: $!\n"; 13838032Speteropendir (QA, ".") || die "$0: can't open directory $queueA for reading: $!\n"; 13938032Speter@dfiles = grep(/^df/, readdir(QA)); 14038032Speter$now = time(); 14138032Speter($program = $0) =~ s,.*/,,; 14238032Speter&openlog($program, 'pid', 'mail'); 14338032Speter 14438032Speter# Loop through the dfiles 14538032Speterwhile ($dfile = pop(@dfiles)) { 14638032Speter print "Checking $dfile\n" if ($debug); 14738032Speter ($qfile = $dfile) =~ s/^d/q/; 14864562Sgshapiro ($xfile = $dfile) =~ s/^d/x/; 14938032Speter ($mfile = $dfile) =~ s/^df//; 15038032Speter if (! -e $qfile || -z $qfile) { 15138032Speter print "$qfile is gone or zero bytes - skipping\n" if ($debug); 15238032Speter next; 15338032Speter } 15438032Speter 15538032Speter ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, 15638032Speter $atime,$mtime,$ctime,$blksize,$blocks) = stat($dfile); 15764562Sgshapiro if (! defined $mtime) { 15864562Sgshapiro print "$dfile is gone - skipping\n" if ($debug); 15964562Sgshapiro next; 16064562Sgshapiro } 16138032Speter 16238032Speter # Compare timestamps 16338032Speter if (($mtime + $age) > $now) { 16438032Speter printf ("%s is %d seconds old - skipping\n", $dfile, $now-$mtime) if ($debug); 16538032Speter next; 16638032Speter } 16738032Speter 16838032Speter # See if files of the same name already exist in $queueB 16938032Speter if (-e "$queueB/$dfile") { 17038032Speter print "$queueb/$dfile already exists - skipping\n" if ($debug); 17138032Speter next; 17238032Speter } 17338032Speter if (-e "$queueB/$qfile") { 17438032Speter print "$queueb/$qfile already exists - skipping\n" if ($debug); 17538032Speter next; 17638032Speter } 17764562Sgshapiro if (-e "$queueB/$xfile") { 17864562Sgshapiro print "$queueb/$xfile already exists - skipping\n" if ($debug); 17964562Sgshapiro next; 18064562Sgshapiro } 18138032Speter 18238032Speter # Try and lock qf* file 18338032Speter unless (open(QF, ">>$qfile")) { 18438032Speter print "$qfile: $!\n" if ($debug); 18538032Speter next; 18638032Speter } 18738032Speter $retval = flock(QF, $LOCK_EX|$LOCK_NB) || ($retval = -1); 18838032Speter if ($retval == -1) { 18938032Speter print "$qfile already flock()ed - skipping\n" if ($debug); 19038032Speter close(QF); 19138032Speter next; 19238032Speter } 19338032Speter print "$qfile now flock()ed\n" if ($debug); 19438032Speter 19564562Sgshapiro # Check df* file again in case sendmail got in 19664562Sgshapiro if (! -e $dfile) { 19764562Sgshapiro print "$mfile sent - skipping\n" if ($debug); 19864562Sgshapiro # qf* file created by ourselves at open? (Almost certainly) 19964562Sgshapiro if (-z $qfile) { 20064562Sgshapiro unlink($qfile); 20164562Sgshapiro } 20264562Sgshapiro close(QF); 20364562Sgshapiro next; 20464562Sgshapiro } 20564562Sgshapiro 20638032Speter # Show time! Do the link()s 20738032Speter if (link("$dfile", "$queueB/$dfile") == 0) { 20864562Sgshapiro $bang = $!; 20964562Sgshapiro &syslog('err', 'link(%s, %s/%s): %s', $dfile, $queueB, $dfile, $bang); 21064562Sgshapiro print STDERR "$0: link($dfile, $queueB/$dfile): $bang\n"; 21138032Speter exit (1); 21238032Speter } 21338032Speter if (link("$qfile", "$queueB/$qfile") == 0) { 21464562Sgshapiro $bang = $!; 21564562Sgshapiro &syslog('err', 'link(%s, %s/%s): %s', $qfile, $queueB, $qfile, $bang); 21664562Sgshapiro print STDERR "$0: link($qfile, $queueB/$qfile): $bang\n"; 21738032Speter unlink("$queueB/$dfile"); 21838032Speter exit (1); 21938032Speter } 22064562Sgshapiro if (-e "$xfile") { 22164562Sgshapiro if (link("$xfile", "$queueB/$xfile") == 0) { 22264562Sgshapiro $bang = $!; 22364562Sgshapiro &syslog('err', 'link(%s, %s/%s): %s', $xfile, $queueB, $xfile, $bang); 22464562Sgshapiro print STDERR "$0: link($xfile, $queueB/$xfile): $bang\n"; 22564562Sgshapiro unlink("$queueB/$dfile"); 22664562Sgshapiro unlink("$queueB/$qfile"); 22764562Sgshapiro exit (1); 22864562Sgshapiro } 22964562Sgshapiro } 23038032Speter 23138032Speter # Links created successfully. Unlink the original files, release the 23238032Speter # lock, and close the file. 23338032Speter print "links ok\n" if ($debug); 23438032Speter if (unlink($qfile) == 0) { 23564562Sgshapiro $bang = $!; 23664562Sgshapiro &syslog('err', 'unlink(%s): %s', $qfile, $bang); 23764562Sgshapiro print STDERR "$0: unlink($qfile): $bang\n"; 23838032Speter exit (1); 23938032Speter } 24038032Speter if (unlink($dfile) == 0) { 24164562Sgshapiro $bang = $!; 24264562Sgshapiro &syslog('err', 'unlink(%s): %s', $dfile, $bang); 24364562Sgshapiro print STDERR "$0: unlink($dfile): $bang\n"; 24438032Speter exit (1); 24538032Speter } 24664562Sgshapiro if (-e "$xfile") { 24764562Sgshapiro if (unlink($xfile) == 0) { 24864562Sgshapiro $bang = $!; 24964562Sgshapiro &syslog('err', 'unlink(%s): %s', $xfile, $bang); 25064562Sgshapiro print STDERR "$0: unlink($xfile): $bang\n"; 25164562Sgshapiro exit (1); 25264562Sgshapiro } 25364562Sgshapiro } 25438032Speter flock(QF, $LOCK_UN); 25538032Speter close(QF); 25638032Speter &syslog('info', '%s moved to %s', $mfile, $queueB); 25738032Speter print "Done with $dfile $qfile\n\n" if ($debug); 25838032Speter} 259