#!/usr/bin/perl # # rc.sc_dbwatch # # version 1.04, 11-16-08 # ################################################################# # WARNING! do not modify this script, make one with a new name. # # This script will be overwritten by subsequent installs of # # SpamCannibal. # ################################################################# # # Copyright 2003 - 2008, Michael Robinton # 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 vars qw($watchconfig $SiteConfig $LogLevel); use lib qw(blib/lib blib/arch); use Unix::Syslog 0.97 qw(:macros :subs); use Mail::SpamCannibal::SiteConfig; use Mail::SpamCannibal::ScriptSupport 0.10 qw( job_died dbjob_chk dbjob_kill dbjob_recover doINCLUDE ); use Mail::SpamCannibal::PidUtil 0.03 qw( is_running get_script_name make_pidfile zap_pidfile ); use Net::DNS::ToolKit qw( ttlAlpha2Num ); use IPTables::IPv4::DBTarpit::Tools; sub usage { print STDERR $_[0],"\n\n" if $_[0]; print STDERR qq| Syntax: $0 start path/to/config.file $0 restart path/to/config.file $0 stop path/to/config.file |; exit 1; } $| = 1; my $action = shift @ARGV; usage() unless $action; my $config = shift @ARGV; usage() unless $config; $watchconfig = doINCLUDE($config); usage('could not load config file') unless $watchconfig; usage('corrupted config file') unless keys %$watchconfig; my $recheck = ttlAlpha2Num($watchconfig->{RECHECK}); usage('missing RECHECK interval') unless $recheck; $recheck = 60 if $recheck < 60; # reasonable value is not less than once per minute my $graceperiod = ttlAlpha2Num($watchconfig->{GRACE_PERIOD}); usage('missing GRACE_PERIOD') unless $graceperiod; $SiteConfig = $watchconfig->{SiteConfig} || new Mail::SpamCannibal::SiteConfig; if ($watchconfig->{SYSLOG}) { $LogLevel = eval "$watchconfig->{SYSLOG}"; } # only open the db's we will need my ($environment,$tarpit,$archive,$contrib,$evidence) = ( $SiteConfig->{SPMCNBL_ENVIRONMENT}, $SiteConfig->{SPMCNBL_DB_TARPIT}, $SiteConfig->{SPMCNBL_DB_ARCHIVE}, $SiteConfig->{SPMCNBL_DB_CONTRIB}, $SiteConfig->{SPMCNBL_DB_EVIDENCE}, ); my %default = ( dbhome => $environment, dbfile => [$tarpit,$archive], txtfile => [$contrib,$evidence], ); my $me = get_script_name(); $0 = $me; # clean up ps and top display info my $pidfile = $environment .'/'. $me . '.pid'; my $run = is_running($pidfile); if ($action eq 'stop' || $action eq 'restart') { kill 15, $run if $run; sleep 1; kill 9, $run if $run && kill 0, $run; $run = 0; openlog($me, LOG_PID(), LOG_MAIL()) if $watchconfig->{SYSLOG}; do_common('STOP'); closelog if $watchconfig->{SYSLOG}; exit 0 if $action eq 'stop'; } elsif ($action ne 'start') { usage(); } if ($run) { print "$me: $run, already running\n"; exit 1; } # fork and test if ( $run = fork ) { waitpid($run,0); exit; } # clean up for proper daemon operation chdir '/'; # allow root dismount open STDIN, '/dev/null' or die "Can't dup STDIN to /dev/null: $!"; open STDOUT, '>/dev/null' or die "Can't dup STDOUT to /dev/null: $!"; open STDERR, '>/dev/null' or die "Can't dup STDERR to /dev/null: $!"; exit 0 if $run = fork; $run = make_pidfile($pidfile); if ($watchconfig->{SYSLOG}) { openlog($me, LOG_PID(), LOG_MAIL()); syslog($LogLevel,"%s\n",'Initiated...'); } my $forcefile = $environment .'/'. get_script_name() . '.startup.pid'; open(F,'>'. $forcefile) or die "could not open startup file $forcefile\n"; print F 0,"\n"; # bogus entry to force rebuild close F; local $SIG{TERM} = sub { $run = 0 }; while ($run) { next if dbjob_chk(\%default); if ($watchconfig->{SYSLOG}) { my %jobs; if (job_died(\%jobs,$environment)) { foreach(keys %jobs) { unless ($jobs{$_} || $_ eq $forcefile) { syslog($LogLevel, "%s failed\n", $_); } } } } dbjob_kill(\%default,$graceperiod); dbjob_recover(\%default); do_common('START'); } continue { sleep $recheck; } if ($watchconfig->{SYSLOG}) { syslog($LogLevel,"%s\n",'caught SIGTERM, exiting...'); closelog(); } zap_pidfile($environment); sub do_common { my($action) = @_; return 0 unless $action eq 'STOP' || $action eq 'START'; for(my $i=0; $i<= $#{$watchconfig->{$action}};$i+=2) { my $cmd = $watchconfig->{$action}->[$i]; my $args = $watchconfig->{$action}->[$i+1]; if ($cmd !~ m|^/|) { $cmd = $SiteConfig->{SPMCNBL_SCRIPT_DIR} .'/'. $cmd; } my $response = (-e $cmd && -x $cmd) ? qx|$cmd $args| : 'not found or not executable'; syslog($LogLevel, "%s, %s\n", $watchconfig->{$action}->[$i], $response) if $response && $watchconfig->{SYSLOG}; } sleep 1 if $action eq 'STOP'; # give cleanup some time return 1; } 1; __END__ =head1 NAME rc.sc_dbwatch - Database watcher =head1 SYNOPSIS rc.sc_dbwatch start rc.sc_dbwatch restart rc.sc_dbwatch stop =head1 DESCRIPTION rc.sc_dbwatch start Start the db watcher and configured db daemons any previously running db tasks are first terminated with a SIGTERM and if the do not respond with a SIGKILL rc.sc_dbwatch restart Previous instance of rc.sc_dbwatch is terminated with a SIGTERM or SIGKILL if unresponsive. Configured db daemons are terminated using the STOP method in the sc_dbwatch.conf file. sc_dbwatch then uses the start method described above to terminate any other db tasks and restart the configured daemons. rc.sc_dbwatch stop Previous instance of rc.sc_dbwatch is terminated with a SIGTERM or SIGKILL if unresponsive. Configured db daemons are terminated using the STOP method in the sc_dbwatch.conf file. The rc.sc_dbwatch daemon expects its configuration file to contain information about all daemons that will use the database. Cron jobs are not configured though they will be blocked or killed if they register their PID and if there is a database fault. =head1 AUTHOR Michael Robinton =head1 COPYRIGHT Copyright 2003 - 2008, Michael Robinton This script is free software; you can redistribute it and/or modify it under the terms of the GPL software license. =cut 1;