use strict;
my $suck;
my $rdate;
my ($my_syslog, $debug, $verbose);
my $start_time = time;
my $mailhost = 'mail';
my $sendmail_queue_dir = '/var/spool/mqueue/'; my $interface = 'ppp0'; my $max_tries = 1; my $retry_delay = 300; my $connect_timeout = 45;
my $log_file = '>/dev/null'; $log_file = '>>/var/log/mailqueue.pl';
my $this_hour = +[localtime()]->[2];
my $pppd = 'pppd';
my $fetchmail = 'fetchmail'; my $sendmail = 'sendmail';
my $fetchmail_pid = '/var/run/fetchmail.pid';
$ENV{'PATH'} = ":/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:";
my $lockfile = "/var/run/mailqueue.lock"; my $space = ' '; my $program_name = $0;
$program_name = substr ($program_name, (rindex ($program_name, '/') + 1));
open SYSLOG, $log_file or die "Could not open $log_file\n\t";
sys_log ("Started by UID $<");
my $pppd_pid;
if ($< != 0) {
sys_log ("Not root...exit");
print STDERR "You are not root...sorry cannot run.\n";
exit (1);
}
sub sys_log {
my ($message) = @_;
print SYSLOG join(' ',
$program_name,
''.localtime(), $message)."\n";
print STDERR $message, "\n" if $debug;
}
$verbose = 1;
for (my $i = 0; $i <= $ if ($ARGV[$i] eq '-v' || $ARGV[$i] eq '-verbose') {
$verbose++;
print "Running in verbose mode level ($verbose).\n";
} elsif ($ARGV[$i] eq '-d' || $ARGV[$i] eq '-debug') {
$debug++;
$verbose = 10; print STDERR "Running in debug mode.\n";
} elsif ($ARGV[$i] eq '-q' || $ARGV[$i] eq '-quiet') {
$debug = 0;
$verbose = 0;
} elsif ($ARGV[$i] eq '-max_tries') {
if (not defined $ARGV[$i + 1]) {
printf STDERR "$0: Error: option -max_tries requires a value.\n";
&usage;
} else {
$max_tries = $ARGV[$i + 1];
}
} elsif ($ARGV[$i] eq '-retry_delay') {
if (not defined $ARGV[$i + 1]) {
printf STDERR "$0: Error: option -retry_delay requires a value.\n";
&usage;
} else {
$max_tries = $ARGV[$i + 1];
}
} elsif ($ARGV[$i] eq '-interface') {
if (not defined $ARGV[$i + 1]) {
printf STDERR "$0: Error: option -interface requires a value.\n";
&usage;
} else {
$max_tries = $ARGV[$i + 1];
}
} elsif ($ARGV[$i] eq '-mailhost') {
if (not defined $ARGV[$i + 1]) {
printf STDERR "$0: Error: option -mailhost requires a value.\n";
&usage;
} else {
$mailhost = $ARGV[$i + 1];
}
} else {
print STDERR "Unknown command line option: [". $ARGV[$i]."]\n";
&usage;
}
}
$| = 1 if $verbose;
&check_program ($my_syslog) || die "$0 -> Error: $my_syslog is required\n";
($fetchmail = &check_program ($fetchmail))
|| die "$0 -> Error: Could not find fetchmail/etrn\n";
($pppd = &check_program ($pppd))
|| die "$0 -> Error: Could not find pppd\n";
(-d $sendmail_queue_dir) || die "$0 -> Error: The sendmail queue directory\n\t[$sendmail_queue_dir] does not exist or is not a directory.\n";
($sendmail = &check_program ($sendmail))
|| die "$0 -> Error: Could not find $sendmail\n";
if (-s $lockfile) {
my $pid = `cat $lockfile`; chop $pid;
if (not &process_is_dead ($pid)) {
print STDERR "$0 -> Process locked by pid $pid killing it.\n"
if $verbose;
kill 15, $pid;
waitpid ($pid, 0); }
sys_log ("Removing stale lock for pid $pid") if $verbose;
unlink ($lockfile) || die $!;
}
open (LOCK, '>'.$lockfile) || die "$0: Could not create lockfile $lockfile\n";
print LOCK $$, "\n";
close LOCK;
if ($debug) {
print STDERR " Max tries: $max_tries\n";
print STDERR " Dial Retry Delay: $retry_delay seconds.\n";
print STDERR "Interface set to watch: $interface\n";
print STDERR " Mailhost set to watch: $mailhost\n";
print STDERR " Connection timeout: $connect_timeout\n";
print STDERR " Sendmail: $sendmail\n";
print STDERR " pppd: $pppd\n";
print STDERR " fetchmail/etrn.pl: $fetchmail\n";
print STDERR "\n\n";
}
((-x $pppd) && (-x $sendmail) && (-x $fetchmail))
|| die "Still some problem with programs.\n\tRun with -d to see if the path is specified for sendmail,\n\tpppd and fetchmail/etrn.pl";
while ($max_tries--) {
my $child_pid;
unless ($child_pid = fork) {
my $count = $connect_timeout;
while (&interface_is_down ($interface) && $count--) {sleep (1)}
if ($count < 1) {exit (1)}
sys_log ("Have connection->sending any local mail.") if $verbose;
system("$sendmail -q");
sys_log ("Checking remote queue on ($mailhost)");
my $result;
my $daemon = 0;
my $pid;
if (defined $fetchmail_pid and -f $fetchmail_pid) {
if (not open PID, $fetchmail_pid) {
sys_log("Could not open $fetchmail_pid");
die}
$pid = <PID>;
if ($pid =~ m|([0-9]+)\s+([0-9]*)|) {
$pid = $1;
$daemon = $2;
}
close PID;
sys_log("Have PID file ($fetchmail_pid) with PID $pid $daemon");
if (&process_is_dead($pid)) {
sys_log(" It is no longer running");
$daemon = 0; $pid = 0}
}
if (not $pid or ($pid and $daemon)) {
sys_log("Running $fetchmail [$daemon]");
my $result = (system ($fetchmail))/256;
sys_log($fetchmail.' exited with status '.$result) if $debug;
} else {
sys_log("$fetchmail already running...");
}
sys_log("Fetchmail done...watching $sendmail_queue_dir");
&watch_dir ($sendmail_queue_dir, 10);
sys_log ("Done polling for mail");
if (-f $fetchmail_pid and not $daemon) {
my $result = `$fetchmail -q`; chop $result;
sys_log($result);
}
exit (0);
}
if (&interface_is_down ($interface) && $pppd_pid == 0) {
sys_log ("Try to connect with pppd") if $debug;
unless ($pppd_pid = fork) {
exec ($pppd.' -detach');
}
}
waitpid ($child_pid, 0);
my $child_status = ($? / 256);
my $child_kill = $? % 256;
if ($child_status == 0) {
if ($this_hour <= 4 and defined $suck) {
sys_log ("Calling suck...");
print `$suck`;
}
if (defined $rdate) {
sys_log ("Calling rtime...");
print `$rdate`;
}
if ($pppd_pid) { sys_log ("Killing pppd (pid $pppd_pid)");
kill 15, $pppd_pid;
waitpid ($pppd_pid, 0); }
sys_log ("Finished with cycle.");
unlink ($lockfile);
sys_log ("Total time: ".(time-$start_time)." seconds") if $debug;
exit (0);
}
if ($pppd_pid && &process_is_dead ($pppd_pid)) {$pppd_pid = 0}
sys_log (join ('', "Warn: Did not connect -> Try ",
$max_tries, " more times...after ",
$retry_delay, " seconds"));
if (not $max_tries) {
sys_log ("Giving up...");
exit (1);
}
sleep ($retry_delay);
sys_log ("ok...trying again.");
}
sub check_program {
my ($program) = @_;
my $exists = 0;
my $path_specified = 0;
my $path;
if ($program =~ /\//) {
$path_specified = 1;
if (-x $program) {$exists = $program}
}
my $exists;
foreach $path (split(/:/, $ENV{'PATH'})) {
next if length ($path) < 3; if (substr ($path, -1, 1) ne '/') {$path .= '/'}
if (-x $path.$program) {$exists = $path.$program; last}
}
if (not $exists) {
if ($path_specified) {
print STDERR "$0 -> Warn: ". $program.
" is not executable or does not exist.\n";
} else {
print STDERR "$0 -> Warn: [$program] was not found in path\n\t".
$ENV{'PATH'}."\n";
}
}
return ($exists);
}
sub process_is_dead {
my ($pid) = @_;
my @results = split (/\n/, `ps $pid 2>/dev/null`);
if (not defined $results[1]) {return 1}
if ($results[1] =~ /zombie/i) {return 1}
return 0;
}
sub interface_is_down {
my ($interface) = @_;
if (`ifconfig $interface` =~ /UP/) {
return 0;
} else {
return 1;
}
}
sub watch_dir {
my $files_like = '^(xf.*)'; my $dir_to_watch = shift;
my $delay = shift;
my $timeout = 120; my $loop_delay = 1;
if ($dir_to_watch !~ m|/$|) {$dir_to_watch .= '/'}
my $flag = $delay;
my $last_total = 0;
while (($flag -= $loop_delay) > 0) {
sleep $loop_delay;
opendir (DIR, $dir_to_watch);
my $file_count = 0;
my $last_data_dl = 500000; foreach my $file (readdir (DIR)) {
next if not -f $dir_to_watch.$file; my @stats = stat($dir_to_watch.$file);
my $m_time = time - $stats[9];
if ($m_time < $last_data_dl) {$last_data_dl = $m_time}
if ($file =~ m|$files_like|) {
sys_log("$file is like $files_like");
$flag = $delay;
}
}
closedir (DIR);
sys_log ("Watch_dir: $flag ($last_data_dl)") if $debug;
if ($last_data_dl > $timeout and $flag == $delay) {
sys_log("Watch_dir: Timed out after $timeout seconds.");
$flag = 0;
}
}
sys_log ("Watch_dir: Done.");
}
sub usage {
print join ("\n",
'mailqueue.pl -- A program to send and receive mail form a sendmail spooler.',
' Requires that you ISP is running sendmail, at lease version 8.6.?.',
' Also requires that you have fetchmail or etrn.pl installed on this system.',
'', 'Command line args (Default in parrens):',
' -v -verbose Run in verbose mode. Can use this arg multiple times.',
' -d -debug Run in debug mode. Sets verbose level to 10',
' -max_tries N Sets the maximum number of connect retries to N. ('.$max_tries.')',
' -retry_delay N Sets the delay between retrying to N seconds. ('. $retry_delay.')',
" -connect_timeout N Sets the connection timeout to N seconds. (". $connect_timeout. ')',
' -interface STR Sets the default interface to STR. ('. $interface. ')',
' -mailhost STR Sets the mailhost to STR. ('. $mailhost.')',
'');
exit (1);
}