![]() ![]() |
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 librariesuse strict;use warnings;use diagnostics;use FindBin;use lib $FindBin::RealBin;# use Getopt::Long;# include HotSaNIC librariesuse 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 startedmy $lastdiagram=$now-$CONFIG{DTIME}+30; # generate all diagrams 30 sec. after startmy $lastconvert=$now-$CONFIG{CTIME}+300; # build thumbnails 5 minutes after startcall_script($CONFIG{DAEMONDIR},"makeindex.pl",0,$CONFIG{LOGDIR}."/makeindex.log",0);while () {$now=time;# BUG: sometimes after HUP'ing the @PIDs array contains undefined valuesif (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 startupif ($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);}