hotsanic |
Subversion Repositories: |
Compare with Previous - Blame - Download
#!/usr/bin/env perl
# $Id: rrdtimer.pl,v 1.17 2004/07/11 22:39:38 bernisys Exp $
# include PERL libraries
use strict;
use warnings;
use diagnostics;
use FindBin;
use lib $FindBin::RealBin;
# use Getopt::Long;
# include HotSaNIC libraries
use lib "./lib";
use HotSaNICparser;
use HotSaNIClog;
# let all outputs be flushed directly
$|=1;
# set commandline correctly
$0="rrdtimer";
HotSaNIClog::set_modulename("rrdtimer");
(my $CALLDIR=$FindBin::RealBin) =~ s/\/rrdtimer//g;
(my $VERSION = '$Revision: 1.17 $') =~ s/.*(\d+\.\d+).*/$1/;
(my $IDENTIFIER = '$Id: rrdtimer.pl,v 1.17 2004/07/11 22:39:38 bernisys Exp $') =~ s/.*,v (.*) \$/$1/;
HotSaNIClog::info("reading & checking config ($CALLDIR/settings) ...");
my %CONFIG=HotSaNICparser::get_config($CALLDIR);
$CONFIG{MODULEDIR}=$CONFIG{DAEMONDIR}."/modules";
$CONFIG{SCHEDULED}=1;
HotSaNIClog::set_timestamping($CONFIG{TIMESTAMPING});
my ($debuglevel,$runmode)=check_for_args(@ARGV);
$CONFIG{DEBUGLEVEL}=HotSaNIClog::get_debuglevel($CONFIG{DEBUGLEVEL});
$CONFIG{DEBUGLEVEL} |= $debuglevel;
HotSaNIClog::set_debuglevel($CONFIG{DEBUGLEVEL});
if (HotSaNIClog::check_debuglevel("MAIN_CONFIG")) { foreach my $k (keys %CONFIG) { HotSaNIClog::info("configuration -> $k=$CONFIG{$k}"); }}
if ($runmode < 128) { exit 0; }
my $PID=HotSaNICparser::get_pid($CONFIG{PIDFILE},"rrdtimer");
if ( $PID>0 ) {
HotSaNIClog::error("Another process is running on PID $PID - daemon stopped.");
exit 1;
}
my $debug="";
if (($runmode&1)>0) {
HotSaNIClog::info("Debug mode enabled!");
HotSaNIClog::info("Identifier: $IDENTIFIER");
HotSaNIClog::info("Logdir: $CONFIG{LOGDIR}");
HotSaNIClog::info("PID-file: $CONFIG{PIDFILE}");
$debug="d";
}
my @PIDs=();
daemonize();
$SIG{TERM} = \&signalhandler;
$SIG{HUP} = \&signalhandler;
$SIG{USR1} = \&signalhandler;
$SIG{USR2} = \&signalhandler;
main_loop();
######################################################################
# #
# SUBROUTINES BEGIN HERE #
# #
######################################################################
######################################################################
#
# signal-handler
# TERM -> terminate daemon and modules
# HUP -> re-configure
# USR1 -> kill daemon only
# USR2 -> kill modules only
#
sub signalhandler {
my ($sig)=@_;
HotSaNIClog::info("$sig received.");
if (defined $sig) {
if (($sig eq "TERM") || ($sig eq "USR2")) {
HotSaNIClog::info("Stopping all running modules.");
if (@PIDs) { kill "TERM", @PIDs; @PIDs=(); }
}
if (($sig eq "TERM") || ($sig eq "USR1")) {
HotSaNIClog::info("Daemon exiting normally.");
if (-e $CONFIG{PIDFILE}) {
unlink $CONFIG{PIDFILE};
HotSaNIClog::info("PID-file removed.");
}
close STDIN;
close STDOUT;
close STDERR;
exit 0;
}
if ($sig eq "HUP") {
HotSaNIClog::info("reconfiguring myself...");
%CONFIG=HotSaNICparser::get_config($CALLDIR);
$CONFIG{MODULEDIR}=$CONFIG{DAEMONDIR}."/modules";
$CONFIG{SCHEDULED}=1;
# TODO:
#
# - kill unused modules
# - start configured modules (if necessary)
# - re-configure all modules
#
}
}
}
######################################################################
#
# the main loop executes all timed scripts:
#
# signal modules/*/read-data every 10 sec. (hardcoded)
# modules/*/diagrams every $DTIME
# convert() every $CTIME if $CONVERTMETHOD is other than "HTML"
# scan_for_modules() every $STIME
#
sub main_loop {
my @modules=();
my %MODULES=();
my $now=time;
my $lastscan=0; # scan for modules directly after being started
my $lastdiagram=$now-$CONFIG{DTIME}+30; # generate all diagrams 30 sec. after start
my $lastconvert=$now-$CONFIG{CTIME}+300; # build thumbnails 5 minutes after start
call_script($CONFIG{DAEMONDIR},"makeindex.pl",0,$CONFIG{LOGDIR}."/makeindex.log",0);
while () {
$now=time;
# BUG: sometimes after HUP'ing the @PIDs array contains undefined values
if (HotSaNIClog::check_debuglevel("MAIN_HEARTBEAT")) { HotSaNIClog::info("main loop running"); }
# Tell modules to sample data. We do this BEFORE starting any module to avoid
# too small sample intervals. At start time @PIDs is empty so this code won't
# be executed until some modules are started.
#
if (@PIDs) {
if (HotSaNIClog::check_debuglevel("MAIN_SIGNAL")) { HotSaNIClog::info("signaling PIDs: ".join(" ",@PIDs)); }
# When no schedule time was configured, signal ALL modules in PARALLEL (may produce high load!)
# Otherwhise serialize the signalling a bit.
#
if ($CONFIG{SCHEDULED} == 0) { kill "SIGUSR1", @PIDs; }
else {
foreach (@PIDs) {
if (defined $_) {
kill "SIGUSR1", $_;
my $wait=int($CONFIG{SCHEDULE_MIN}+rand($CONFIG{SCHEDULE_MAX}-$CONFIG{SCHEDULE_MIN}))/1000;
if (HotSaNIClog::check_debuglevel("MAIN_SIGNAL_VERBOSE")) { HotSaNIClog::info("\"$MODULES{$_}\" called, sleeping $wait sec."); }
select(undef,undef,undef,$wait);
}
}
}
}
# scan for modules and (re-)start them if necessary
#
if ($lastscan+$CONFIG{STIME} <= $now) {
@modules=HotSaNICparser::scan_for_modules($CONFIG{MODULEDIR},$CONFIG{RUN});
%MODULES=call_modules(@modules);
@PIDs=keys(%MODULES);
$lastscan=int($now/$CONFIG{STIME})*$CONFIG{STIME};
if ($debug ne "d") { logrotate(); }
}
# check if diagrams have to be built.
#
if ($lastdiagram+$CONFIG{DTIME} <= $now) {
my $append;
if ($CONFIG{DIAGRAMLOG} eq "all") { $append=1; }
call_script($CONFIG{DAEMONDIR},"diagrams.pl",5,$CONFIG{LOGDIR}."/diagram.log",$append);
$lastdiagram=int($now/$CONFIG{DTIME})*$CONFIG{DTIME};
}
# check if thumbnails have to be generated.
#
if (($lastconvert+$CONFIG{CTIME} <= $now) && ($CONFIG{CONVERTMETHOD} ne "HTML")) {
call_script($CONFIG{DAEMONDIR},"convert.pl",0,$CONFIG{LOGDIR}."/convert.log",0);
$lastconvert=int($now/$CONFIG{CTIME})*$CONFIG{CTIME};
}
# sleep until system time's next full 10 seconds
#
my $sleeptime=10-(time % 10);
sleep $sleeptime;
}
}
######################################################################
#
# start read-data.pl script in each module
#
sub call_modules {
my (@modules)=@_;
my %MODULES;
my $nicelevel="0";
my $have_started=0;
if (HotSaNIClog::check_debuglevel("MAIN_SCAN_MODULES")) { HotSaNIClog::info("Checking modules..."); }
# iterate through all configured modules...
#
for my $module (@modules) {
my $verbose=0;
if (HotSaNIClog::check_debuglevel("MAIN_SCAN_MODULES_VERBOSE")) { $verbose=1; }
chdir $CONFIG{MODULEDIR}."/$module";
my $PID=HotSaNICparser::get_pid();
# if not running, start the module and wait for PID-file to be generated
#
if ($PID == 0) {
if (HotSaNIClog::check_debuglevel("MAIN_SCAN_MODULES")) { HotSaNIClog::info("starting module \"$module\""); }
if (HotSaNIClog::check_debuglevel("MAIN_SCAN_MODULES_VERBOSE")) { HotSaNIClog::info("----- begin start module \"$module\" -----"); }
$verbose=1;
if (-e "./running.pid") {
if (HotSaNIClog::check_debuglevel("MAIN_SCAN_MODULES_VERBOSE")) { HotSaNIClog::info("removing old PID-file"); }
unlink "./running.pid";
}
my $EXEC="";
if (-e "./read-data-wrapper.pl") { $EXEC="nice -$nicelevel ./read-data-wrapper.pl start $module"; }
elsif (-e "./read-data.pl") { $EXEC="nice -$nicelevel ./read-data.pl start $module"; }
else {
HotSaNIClog::error("cannot find read-data script for module \"$module\"");
next;
}
system "$EXEC";
# wait for module writing its PID-file
#
my $count=0;
while ( ($count<80) && (! -s "./running.pid") ) {
if ($count == 0) { HotSaNIClog::info("waiting for module's PID-file"); }
$count++;
print "." if ($count%5 == 0);
select(undef,undef,undef,0.25);
}
print "\r" if ($count > 0);
$PID=HotSaNICparser::get_pid();
#if PID is valid -> sample for the first time after startup
if ($PID > 0) { kill "SIGUSR1",$PID; }
HotSaNIClog::info("----- end start module \"$module\" -----");
$have_started++;
}
if ($PID >0) {
if ($verbose>0) { HotSaNIClog::info("\"$module\" running on PID $PID"); }
$MODULES{$PID}=$module;
}
else { HotSaNIClog::error("PID for \"$module\" could not be determined"); }
}
if ($have_started>0) { HotSaNIClog::info("-" x 75); }
return %MODULES;
}
######################################################################
#
# call external script (incl. sanity checking etc)
#
sub call_script {
my ($path,$script,$nicelevel,$output,$append)=@_;
if (! defined $append) { $append=0; }
if (! defined $output) { $output=""; }
if (! defined $nicelevel) { $nicelevel=0; }
if ( (defined $path) && (defined $script) ) {
if (HotSaNIClog::check_debuglevel("MAIN_CALL_SCRIPT")) { HotSaNIClog::info("executing \"$path/$script\" with nice $nicelevel"); }
if ( -e "$path/$script") {
if ($output ne "") {
my $logaction="appending";
if (index("1 yes true",lc $append) < 0) {
rename $output,"$output.old";
$logaction="logging";
}
if (HotSaNIClog::check_debuglevel("MAIN_CALL_SCRIPT")) { HotSaNIClog::info("$logaction output to $output"); }
if ($nicelevel ne 0) {system ("cd \"$path\"; nice -$nicelevel ./$script >$output 2>&1 &"); }
else {system ("cd \"$path\"; ./$script >$output 2>&1 &"); }
}
else {
if ($nicelevel ne 0) {system ("cd \"$path\"; nice -$nicelevel ./$script &"); }
else {system ("cd \"$path\"; ./$script &"); }
}
}
else { HotSaNIClog::error("script \"$path/$script\" not found! Check your installation."); }
}
}
######################################################################
#
# fork into background
#
sub daemonize {
use POSIX qw(setsid);
HotSaNIClog::info("entering daemon mode...");
if ($debug eq "") {
close STDIN;
close STDOUT;
close STDERR;
if (open(DEVTTY, "/dev/tty")) { ioctl(DEVTTY,0x20007471,0); close DEVTTY; }
open STDIN,"/dev/null";
open STDOUT,">>".$CONFIG{LOGDIR}."/HotSaNIC.log";
open STDERR,">&STDOUT";
setpgrp(0,$$);
chdir "/";
fork && exit 0;
setsid;
HotSaNIClog::info("archiver successfully forked into background and running on PID $$");
}
else { HotSaNIClog::info("archiver running in debug-mode on PID $$"); }
HotSaNIClog::info("-" x 75);
open FILE,">".$CONFIG{PIDFILE};
print FILE $$;
close FILE;
}
######################################################################
#
# rotate logfiles
#
sub logrotate {
my ($size,$file);
if ($debug ne "d") {
$file=$CONFIG{LOGDIR}."/HotSaNIC.log";
(undef,undef,undef,undef,undef,undef,undef,$size,undef,undef,undef,undef,undef) = stat($file);
if (defined $size) {
if ($size > $CONFIG{LOGSIZE}) {
HotSaNIClog::info("$file exceeding $CONFIG{LOGSIZE} bytes - rotating - keeping $CONFIG{LOGBACKUPS} backups.");
for (my $nn=$CONFIG{LOGBACKUPS};$nn>1;$nn--) {
if ( -e "$file.$nn" ) { unlink "$file.$nn"; }
if ( -e "$file.".($nn-1) ) { rename "$file.".($nn-1),"$file.".$nn; }
}
close STDOUT;
rename $file,"$file.1";
open STDOUT,">>$file";
close STDERR;
rename $file,"$file.1";
open STDERR,">>$file";
}
}
}
}
######################################################################
#
# evaluate commandline arguments
#
sub check_for_args {
HotSaNIClog::info("evaluating cmdline arguments...");
my @args=@_;
# set initial values
#
my $debuglevel=0;
my $runmode=0;
# Check if we were called with any options
#
foreach my $arg (@args) {
if (index($arg,"d") >= 0) { $runmode|=1; }
if (index($arg,"D") >= 0) { $runmode|=128; }
if (index($arg,"h") >= 0) { $runmode|=256; }
if (index($arg,"?") >= 0) { $runmode|=256; }
$arg=~ s/[a-zA-Z]//g;
if ($arg =~ /[0-9]/) { if ($arg > 0) { $debuglevel=$arg; } }
}
if (($runmode == 0) || ($runmode > 255)) {
print "rrdtimer - CVS version $VERSION ($IDENTIFIER) - OS: $^O\n";
print "usage:\nrrdtimer [options[debuglevel]]\n options:\n";
print " D - enter daemon-mode (i.e. start the main loop)\n";
print " d - enter debugging mode (don't fork into background)\n";
print "\n";
exit 0;
}
return ($debuglevel,$runmode);
}