#!/usr/bin/perl use Fcntl ':flock'; $QMAIL = "/var/qmail"; $CONTROL = "$QMAIL/control"; open LOG, ">>", "/tmp/qmail-remote-log"; { my $ofh = select LOG; $|=1; select $ofh; } Log("starting"); $msgs = 0; $time = 0; load_policy(); while(1) { my @last = grep {$_ > time() - $time} read_log(); Log("status " . @last . "/$msgs"); if (@last >= $msgs) { # Log("sleeping"); } else { last; } while (@last >= $msgs) { my $oldest = $last[0]; my $age = time() - $oldest; # When will the oldest message expire off the queue? # The end of the queue is $time seconds ago # The age of the oldest message is now time() - $oldest # The oldest message will expire in $time - $age seconds my $zzz = $time - $age + int(rand(3)); $zzz = 1 if $zzz < 1; # Log("Sleeping for $zzz secs"); sleep $zzz; shift @last while @last && $last[0] < time() - $time; load_policy(); } } append_log(time()); #if (open F, ">>", "/tmp/qmail-remote-log") { # print F time(), " @ARGV\n"; #} Log("sending @ARGV"); exec "/var/qmail/bin/qmail-remote.real", @ARGV; exit 0; sub fail { my $msg = shift; Log("fail: $msg"); print "Z$msg"; exit 0; } my $last_policy_load ; sub load_policy { my $POLICY = "$CONTROL/outratelimit"; return if ((stat($POLICY))[9] <= $last_policy_load); if (open F, "<", $POLICY) { chomp(my $line = ); ($msgs, $time) = split m{/}, $line; if (defined $last_policy_load) { Log("Policy changed to $msgs/$time"); } $last_policy_load = time(); close F; } } sub read_log { lock_semaphore(LOCK_SH); my @last; if (open(F, "+<", "$CONTROL/outlog")) { chomp(@last = ); close F; } lock_semaphore(LOCK_UN); return @last; } sub append_log { lock_semaphore(LOCK_EX); if (open(F, "+<", "$CONTROL/outlog")) { my @last = grep {$_ > time() - $time} ; chomp(@last); truncate F, 0; seek F, 0, 0; print F join "\n", @last, @_, ""; close F; } lock_semaphore(LOCK_UN); } sub lock_semaphore { if (open SEM, ">", "$CONTROL/qmail-remote-semaphore") { flock(SEM, $_[0]) or fail("Couldn't lock semaphore: $!"); } else { fail("Couldn't open semaphore: $!"); } } sub Log { my @args = @_; tr/\n//d for @args; print LOG time(), " $$ @args\n"; }