#!/usr/bin/perl
#
# rc.multi_dnsbl
# version 1.07, 3-6-10
#
#
#################################################################
# WARNING! do not modify this script, make one with a new name. #
# This script will be overwritten by subsequent installs of     #
# SpamCannibal.                                                 #
#################################################################
#
# Copyright 2003 - 2010, Michael Robinton <michael@bizsystems.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#

use strict;
#use diagnostics;
use lib qw(blib/lib blib/arch);
use Socket;
use Net::DNSBL::MultiDaemon qw(
	run
	$D_VERBOSE
);

use Net::DNSBL::Utilities qw(
	statinit
	cntinit
	list2hash
	doINCLUDE
	open_udpNB
);
my $daemon = 0;
my $LogLevel = 0;

sub usage {
  if ($daemon) {
    if ($LogLevel) {
      syslog($LogLevel,"%s\n",$_[0]);
      closelog();
    }
  } else {
    print STDERR $_[0],"\n" if $_[0];
    print STDERR qq|
Syntax:	$0 start    /path/to/config.file
	$0 start -v /path/to/config.file
	$0 stop     /path/to/config.file
	$0 restart  /path/to/config.file

The -v switch will print the scripts 
actions verbosely to the STDERR.

|;
  }
  exit 1;
}

$| = 1;

usage() if @ARGV < 2;

my $VERBOSE = 0;
my $command;
my $config = '';

while($_ = shift @ARGV) {
  if ($_ eq '-v') {
    $VERBOSE = $D_VERBOSE;
  }
  else {
    $command = $config;
    $config = $_;
  }
}
usage('bad command') unless
	$command eq 'start' ||
	$command eq 'stop' ||
	$command eq 'restart';

usage() unless $config;
usage('path to config must be absolute')
	unless $config =~ m|^/|;

# bind these to all local subroutines
my ($DNSBL,%STATS,$StatStamp,$pidfile,$RUN,$pid);

local $SIG{HUP} = sub {$RUN = 0; $command = 'received sig HUP'};
local $SIG{TERM} = sub {$RUN = 0; $command = 'exiting... sig TERM'};

$0 =~ m|([^/]+)$|;
my $me = $1;		# script name
$me =~ s/rc\.//;
$0 = $me;

while (1) {

  $DNSBL = doINCLUDE($config);
  usage('could not load config file')
	unless $DNSBL;
  usage('corrupted config file')
	unless  keys %$DNSBL;
  usage('no zone name')
	unless $DNSBL->{MDzone};
  cntinit($DNSBL,\%STATS);
  list2hash($DNSBL->{BBC},\%STATS)
	if $DNSBL->{BBC} && ref $DNSBL->{BBC} eq 'ARRAY' && @{$DNSBL->{BBC}};
  usage('statfile path does not exist or is not writable')
	unless ($StatStamp = statinit($DNSBL->{MDstatfile},\%STATS));
  $DNSBL->{MDstatrefresh} = 300 unless $DNSBL->{MDstatrefresh};
  $DNSBL->{MDport}	  = 9953 unless $DNSBL->{MDport};
  $pidfile = $DNSBL->{MDpidpath} .'/'. $me . '.pid';    
  local *PID;
  my $running = 0;
  if (	-e $pidfile &&
	-r $pidfile &&
	open(PID,$pidfile)) {
    $pid = <PID> || 0;
    close PID;
    chomp $pid;
    if ($pid and kill(0, $pid)) {
      $running = 1
    } else {
      $pid = 0;
    }
  }
  if ($command eq 'start') {
    print STDERR "$pid already running"
	if $running && ! $daemon;
    unless ($pid) {
      $pid = fork;
      usage("could not fork") if $pid < 0;	# FATAL
      if ($pid) {
	waitpid($pid,0);
	exit 0;
      }
      $daemon = 1;
# set daemon for disconnect
      chdir '/';			# root dismount
      local *Null;
      if (open(Null,'/dev/null')) {	# IO closed
	open(STDIN,'>&Null') || close STDIN;
	open(STDOUT,'>&Null') || close STDOUT;
	unless ($VERBOSE) {
	  open(STDERR,'>&Null') || close STDERR;
	}
      } else {
	close STDIN;
	close STDOUT;
	close STDERR unless $VERBOSE;
      }
      if($pid = fork) {			# release child to 'init'
	exit 0;
      }
      $pid = $$;
    }
    open(PID,'>'.$pidfile)
	or usage("could not open $pidfile");
    print PID $pid,"\n";
    close PID;
    if ($DNSBL->{MDsyslog}) {		# if logging requested
      require Unix::Syslog;
      import Unix::Syslog @Unix::Syslog::EXPORT_OK;
      $LogLevel = eval "$DNSBL->{MDsyslog}";
      openlog($me, LOG_PID(), LOG_MAIL());
      syslog($LogLevel,"%s\n",'Initiated...');
    }
    (my $L = open_udpNB())
	or usage("could not open listening UDP socket");
    bind($L,sockaddr_in($DNSBL->{MDport},inet_aton($DNSBL->{MDipaddr})))
	or usage("could not bind listening UDP listening port");
    (my $R = open_udpNB())
	or usage("could not open unbound UDP send socket");
    $RUN = $DNSBL->{MDstatrefresh};
    $command = 'internal ERROR';
    run($DNSBL->{MDzone},$L,$R,$DNSBL,\%STATS,\$RUN,$DNSBL->{MDstatfile},$StatStamp,$VERBOSE);
    close $L;
    close $R;
  }
  if ($command eq 'stop' || $command eq 'restart') {	# can not be daemonized yet
    if ($pid) {
      kill 15, $pid;
      $pid = 0;
      sleep 1;
    } else {
      print STDERR "$me: not running\n";
    }
  }
  if ($LogLevel) {	# set by 'start'
    syslog($LogLevel,"%s\n",$command);
    closelog() unless $command =~ /HUP/;
  }
  unlink $pidfile if $pidfile;

  exit 1 if $command =~ /ERROR/;
  exit 0 if $command =~ /TERM/;
  exit 0 if $command eq 'stop';
# restart, HUP fall through

  $command = 'start';
}