| 1 | 1 | simandl | #!/usr/bin/env perl |
| 2 | | | |
| 3 | | | # $Id: rrdtimer.pl,v 1.17 2004/07/11 22:39:38 bernisys Exp $ |
| 4 | | | |
| 5 | | | # include PERL libraries |
| 6 | | | use strict; |
| 7 | | | use warnings; |
| 8 | | | use diagnostics; |
| 9 | | | |
| 10 | | | use FindBin; |
| 11 | | | use lib $FindBin::RealBin; |
| 12 | | | # use Getopt::Long; |
| 13 | | | |
| 14 | | | # include HotSaNIC libraries |
| 15 | | | use lib "./lib"; |
| 16 | | | use HotSaNICparser; |
| 17 | | | use HotSaNIClog; |
| 18 | | | |
| 19 | | | # let all outputs be flushed directly |
| 20 | | | $|=1; |
| 21 | | | |
| 22 | | | # set commandline correctly |
| 23 | | | $0="rrdtimer"; |
| 24 | | | HotSaNIClog::set_modulename("rrdtimer"); |
| 25 | | | |
| 26 | | | (my $CALLDIR=$FindBin::RealBin) =~ s/\/rrdtimer//g; |
| 27 | | | (my $VERSION = '$Revision: 1.17 $') =~ s/.*(\d+\.\d+).*/$1/; |
| 28 | | | (my $IDENTIFIER = '$Id: rrdtimer.pl,v 1.17 2004/07/11 22:39:38 bernisys Exp $') =~ s/.*,v (.*) \$/$1/; |
| 29 | | | |
| 30 | | | HotSaNIClog::info("reading & checking config ($CALLDIR/settings) ..."); |
| 31 | | | my %CONFIG=HotSaNICparser::get_config($CALLDIR); |
| 32 | | | $CONFIG{MODULEDIR}=$CONFIG{DAEMONDIR}."/modules"; |
| 33 | | | $CONFIG{SCHEDULED}=1; |
| 34 | | | HotSaNIClog::set_timestamping($CONFIG{TIMESTAMPING}); |
| 35 | | | |
| 36 | | | my ($debuglevel,$runmode)=check_for_args(@ARGV); |
| 37 | | | $CONFIG{DEBUGLEVEL}=HotSaNIClog::get_debuglevel($CONFIG{DEBUGLEVEL}); |
| 38 | | | $CONFIG{DEBUGLEVEL} |= $debuglevel; |
| 39 | | | HotSaNIClog::set_debuglevel($CONFIG{DEBUGLEVEL}); |
| 40 | | | |
| 41 | | | if (HotSaNIClog::check_debuglevel("MAIN_CONFIG")) { foreach my $k (keys %CONFIG) { HotSaNIClog::info("configuration -> $k=$CONFIG{$k}"); }} |
| 42 | | | if ($runmode < 128) { exit 0; } |
| 43 | | | |
| 44 | | | my $PID=HotSaNICparser::get_pid($CONFIG{PIDFILE},"rrdtimer"); |
| 45 | | | if ( $PID>0 ) { |
| 46 | | | HotSaNIClog::error("Another process is running on PID $PID - daemon stopped."); |
| 47 | | | exit 1; |
| 48 | | | } |
| 49 | | | |
| 50 | | | my $debug=""; |
| 51 | | | if (($runmode&1)>0) { |
| 52 | | | HotSaNIClog::info("Debug mode enabled!"); |
| 53 | | | HotSaNIClog::info("Identifier: $IDENTIFIER"); |
| 54 | | | HotSaNIClog::info("Logdir: $CONFIG{LOGDIR}"); |
| 55 | | | HotSaNIClog::info("PID-file: $CONFIG{PIDFILE}"); |
| 56 | | | $debug="d"; |
| 57 | | | } |
| 58 | | | |
| 59 | | | my @PIDs=(); |
| 60 | | | |
| 61 | | | daemonize(); |
| 62 | | | |
| 63 | | | $SIG{TERM} = \&signalhandler; |
| 64 | | | $SIG{HUP} = \&signalhandler; |
| 65 | | | $SIG{USR1} = \&signalhandler; |
| 66 | | | $SIG{USR2} = \&signalhandler; |
| 67 | | | |
| 68 | | | main_loop(); |
| 69 | | | |
| 70 | | | |
| 71 | | | ###################################################################### |
| 72 | | | # # |
| 73 | | | # SUBROUTINES BEGIN HERE # |
| 74 | | | # # |
| 75 | | | ###################################################################### |
| 76 | | | |
| 77 | | | |
| 78 | | | ###################################################################### |
| 79 | | | # |
| 80 | | | # signal-handler |
| 81 | | | # TERM -> terminate daemon and modules |
| 82 | | | # HUP -> re-configure |
| 83 | | | # USR1 -> kill daemon only |
| 84 | | | # USR2 -> kill modules only |
| 85 | | | # |
| 86 | | | sub signalhandler { |
| 87 | | | my ($sig)=@_; |
| 88 | | | |
| 89 | | | HotSaNIClog::info("$sig received."); |
| 90 | | | if (defined $sig) { |
| 91 | | | if (($sig eq "TERM") || ($sig eq "USR2")) { |
| 92 | | | HotSaNIClog::info("Stopping all running modules."); |
| 93 | | | if (@PIDs) { kill "TERM", @PIDs; @PIDs=(); } |
| 94 | | | } |
| 95 | | | |
| 96 | | | if (($sig eq "TERM") || ($sig eq "USR1")) { |
| 97 | | | HotSaNIClog::info("Daemon exiting normally."); |
| 98 | | | if (-e $CONFIG{PIDFILE}) { |
| 99 | | | unlink $CONFIG{PIDFILE}; |
| 100 | | | HotSaNIClog::info("PID-file removed."); |
| 101 | | | } |
| 102 | | | close STDIN; |
| 103 | | | close STDOUT; |
| 104 | | | close STDERR; |
| 105 | | | exit 0; |
| 106 | | | } |
| 107 | | | |
| 108 | | | if ($sig eq "HUP") { |
| 109 | | | HotSaNIClog::info("reconfiguring myself..."); |
| 110 | | | %CONFIG=HotSaNICparser::get_config($CALLDIR); |
| 111 | | | $CONFIG{MODULEDIR}=$CONFIG{DAEMONDIR}."/modules"; |
| 112 | | | $CONFIG{SCHEDULED}=1; |
| 113 | | | # TODO: |
| 114 | | | # |
| 115 | | | # - kill unused modules |
| 116 | | | # - start configured modules (if necessary) |
| 117 | | | # - re-configure all modules |
| 118 | | | # |
| 119 | | | } |
| 120 | | | } |
| 121 | | | } |
| 122 | | | |
| 123 | | | |
| 124 | | | ###################################################################### |
| 125 | | | # |
| 126 | | | # the main loop executes all timed scripts: |
| 127 | | | # |
| 128 | | | # signal modules/*/read-data every 10 sec. (hardcoded) |
| 129 | | | # modules/*/diagrams every $DTIME |
| 130 | | | # convert() every $CTIME if $CONVERTMETHOD is other than "HTML" |
| 131 | | | # scan_for_modules() every $STIME |
| 132 | | | # |
| 133 | | | sub main_loop { |
| 134 | | | my @modules=(); |
| 135 | | | my %MODULES=(); |
| 136 | | | |
| 137 | | | my $now=time; |
| 138 | | | my $lastscan=0; # scan for modules directly after being started |
| 139 | | | my $lastdiagram=$now-$CONFIG{DTIME}+30; # generate all diagrams 30 sec. after start |
| 140 | | | my $lastconvert=$now-$CONFIG{CTIME}+300; # build thumbnails 5 minutes after start |
| 141 | | | |
| 142 | | | call_script($CONFIG{DAEMONDIR},"makeindex.pl",0,$CONFIG{LOGDIR}."/makeindex.log",0); |
| 143 | | | |
| 144 | | | while () { |
| 145 | | | $now=time; |
| 146 | | | |
| 147 | | | # BUG: sometimes after HUP'ing the @PIDs array contains undefined values |
| 148 | | | |
| 149 | | | if (HotSaNIClog::check_debuglevel("MAIN_HEARTBEAT")) { HotSaNIClog::info("main loop running"); } |
| 150 | | | |
| 151 | | | # Tell modules to sample data. We do this BEFORE starting any module to avoid |
| 152 | | | # too small sample intervals. At start time @PIDs is empty so this code won't |
| 153 | | | # be executed until some modules are started. |
| 154 | | | # |
| 155 | | | if (@PIDs) { |
| 156 | | | if (HotSaNIClog::check_debuglevel("MAIN_SIGNAL")) { HotSaNIClog::info("signaling PIDs: ".join(" ",@PIDs)); } |
| 157 | | | |
| 158 | | | # When no schedule time was configured, signal ALL modules in PARALLEL (may produce high load!) |
| 159 | | | # Otherwhise serialize the signalling a bit. |
| 160 | | | # |
| 161 | | | if ($CONFIG{SCHEDULED} == 0) { kill "SIGUSR1", @PIDs; } |
| 162 | | | else { |
| 163 | | | foreach (@PIDs) { |
| 164 | | | if (defined $_) { |
| 165 | | | kill "SIGUSR1", $_; |
| 166 | | | my $wait=int($CONFIG{SCHEDULE_MIN}+rand($CONFIG{SCHEDULE_MAX}-$CONFIG{SCHEDULE_MIN}))/1000; |
| 167 | | | if (HotSaNIClog::check_debuglevel("MAIN_SIGNAL_VERBOSE")) { HotSaNIClog::info("\"$MODULES{$_}\" called, sleeping $wait sec."); } |
| 168 | | | select(undef,undef,undef,$wait); |
| 169 | | | } |
| 170 | | | } |
| 171 | | | } |
| 172 | | | } |
| 173 | | | |
| 174 | | | # scan for modules and (re-)start them if necessary |
| 175 | | | # |
| 176 | | | if ($lastscan+$CONFIG{STIME} <= $now) { |
| 177 | | | @modules=HotSaNICparser::scan_for_modules($CONFIG{MODULEDIR},$CONFIG{RUN}); |
| 178 | | | %MODULES=call_modules(@modules); |
| 179 | | | @PIDs=keys(%MODULES); |
| 180 | | | $lastscan=int($now/$CONFIG{STIME})*$CONFIG{STIME}; |
| 181 | | | if ($debug ne "d") { logrotate(); } |
| 182 | | | } |
| 183 | | | |
| 184 | | | # check if diagrams have to be built. |
| 185 | | | # |
| 186 | | | if ($lastdiagram+$CONFIG{DTIME} <= $now) { |
| 187 | | | my $append; |
| 188 | | | if ($CONFIG{DIAGRAMLOG} eq "all") { $append=1; } |
| 189 | | | call_script($CONFIG{DAEMONDIR},"diagrams.pl",5,$CONFIG{LOGDIR}."/diagram.log",$append); |
| 190 | | | $lastdiagram=int($now/$CONFIG{DTIME})*$CONFIG{DTIME}; |
| 191 | | | } |
| 192 | | | |
| 193 | | | # check if thumbnails have to be generated. |
| 194 | | | # |
| 195 | | | if (($lastconvert+$CONFIG{CTIME} <= $now) && ($CONFIG{CONVERTMETHOD} ne "HTML")) { |
| 196 | | | call_script($CONFIG{DAEMONDIR},"convert.pl",0,$CONFIG{LOGDIR}."/convert.log",0); |
| 197 | | | $lastconvert=int($now/$CONFIG{CTIME})*$CONFIG{CTIME}; |
| 198 | | | } |
| 199 | | | |
| 200 | | | # sleep until system time's next full 10 seconds |
| 201 | | | # |
| 202 | | | my $sleeptime=10-(time % 10); |
| 203 | | | sleep $sleeptime; |
| 204 | | | } |
| 205 | | | } |
| 206 | | | |
| 207 | | | |
| 208 | | | ###################################################################### |
| 209 | | | # |
| 210 | | | # start read-data.pl script in each module |
| 211 | | | # |
| 212 | | | sub call_modules { |
| 213 | | | my (@modules)=@_; |
| 214 | | | my %MODULES; |
| 215 | | | my $nicelevel="0"; |
| 216 | | | my $have_started=0; |
| 217 | | | |
| 218 | | | if (HotSaNIClog::check_debuglevel("MAIN_SCAN_MODULES")) { HotSaNIClog::info("Checking modules..."); } |
| 219 | | | |
| 220 | | | # iterate through all configured modules... |
| 221 | | | # |
| 222 | | | for my $module (@modules) { |
| 223 | | | my $verbose=0; |
| 224 | | | if (HotSaNIClog::check_debuglevel("MAIN_SCAN_MODULES_VERBOSE")) { $verbose=1; } |
| 225 | | | chdir $CONFIG{MODULEDIR}."/$module"; |
| 226 | | | |
| 227 | | | my $PID=HotSaNICparser::get_pid(); |
| 228 | | | |
| 229 | | | # if not running, start the module and wait for PID-file to be generated |
| 230 | | | # |
| 231 | | | if ($PID == 0) { |
| 232 | | | if (HotSaNIClog::check_debuglevel("MAIN_SCAN_MODULES")) { HotSaNIClog::info("starting module \"$module\""); } |
| 233 | | | if (HotSaNIClog::check_debuglevel("MAIN_SCAN_MODULES_VERBOSE")) { HotSaNIClog::info("----- begin start module \"$module\" -----"); } |
| 234 | | | $verbose=1; |
| 235 | | | if (-e "./running.pid") { |
| 236 | | | if (HotSaNIClog::check_debuglevel("MAIN_SCAN_MODULES_VERBOSE")) { HotSaNIClog::info("removing old PID-file"); } |
| 237 | | | unlink "./running.pid"; |
| 238 | | | } |
| 239 | | | my $EXEC=""; |
| 240 | | | if (-e "./read-data-wrapper.pl") { $EXEC="nice -$nicelevel ./read-data-wrapper.pl start $module"; } |
| 241 | | | elsif (-e "./read-data.pl") { $EXEC="nice -$nicelevel ./read-data.pl start $module"; } |
| 242 | | | else { |
| 243 | | | HotSaNIClog::error("cannot find read-data script for module \"$module\""); |
| 244 | | | next; |
| 245 | | | } |
| 246 | | | system "$EXEC"; |
| 247 | | | |
| 248 | | | # wait for module writing its PID-file |
| 249 | | | # |
| 250 | | | my $count=0; |
| 251 | | | while ( ($count<80) && (! -s "./running.pid") ) { |
| 252 | | | if ($count == 0) { HotSaNIClog::info("waiting for module's PID-file"); } |
| 253 | | | $count++; |
| 254 | | | print "." if ($count%5 == 0); |
| 255 | | | select(undef,undef,undef,0.25); |
| 256 | | | } |
| 257 | | | print "\r" if ($count > 0); |
| 258 | | | $PID=HotSaNICparser::get_pid(); |
| 259 | | | #if PID is valid -> sample for the first time after startup |
| 260 | | | if ($PID > 0) { kill "SIGUSR1",$PID; } |
| 261 | | | HotSaNIClog::info("----- end start module \"$module\" -----"); |
| 262 | | | $have_started++; |
| 263 | | | } |
| 264 | | | |
| 265 | | | if ($PID >0) { |
| 266 | | | if ($verbose>0) { HotSaNIClog::info("\"$module\" running on PID $PID"); } |
| 267 | | | $MODULES{$PID}=$module; |
| 268 | | | } |
| 269 | | | else { HotSaNIClog::error("PID for \"$module\" could not be determined"); } |
| 270 | | | } |
| 271 | | | if ($have_started>0) { HotSaNIClog::info("-" x 75); } |
| 272 | | | return %MODULES; |
| 273 | | | } |
| 274 | | | |
| 275 | | | |
| 276 | | | ###################################################################### |
| 277 | | | # |
| 278 | | | # call external script (incl. sanity checking etc) |
| 279 | | | # |
| 280 | | | sub call_script { |
| 281 | | | my ($path,$script,$nicelevel,$output,$append)=@_; |
| 282 | | | |
| 283 | | | if (! defined $append) { $append=0; } |
| 284 | | | if (! defined $output) { $output=""; } |
| 285 | | | if (! defined $nicelevel) { $nicelevel=0; } |
| 286 | | | |
| 287 | | | if ( (defined $path) && (defined $script) ) { |
| 288 | | | if (HotSaNIClog::check_debuglevel("MAIN_CALL_SCRIPT")) { HotSaNIClog::info("executing \"$path/$script\" with nice $nicelevel"); } |
| 289 | | | if ( -e "$path/$script") { |
| 290 | | | if ($output ne "") { |
| 291 | | | my $logaction="appending"; |
| 292 | | | if (index("1 yes true",lc $append) < 0) { |
| 293 | | | rename $output,"$output.old"; |
| 294 | | | $logaction="logging"; |
| 295 | | | } |
| 296 | | | if (HotSaNIClog::check_debuglevel("MAIN_CALL_SCRIPT")) { HotSaNIClog::info("$logaction output to $output"); } |
| 297 | | | if ($nicelevel ne 0) {system ("cd \"$path\"; nice -$nicelevel ./$script >$output 2>&1 &"); } |
| 298 | | | else {system ("cd \"$path\"; ./$script >$output 2>&1 &"); } |
| 299 | | | } |
| 300 | | | else { |
| 301 | | | if ($nicelevel ne 0) {system ("cd \"$path\"; nice -$nicelevel ./$script &"); } |
| 302 | | | else {system ("cd \"$path\"; ./$script &"); } |
| 303 | | | } |
| 304 | | | } |
| 305 | | | else { HotSaNIClog::error("script \"$path/$script\" not found! Check your installation."); } |
| 306 | | | } |
| 307 | | | } |
| 308 | | | |
| 309 | | | |
| 310 | | | ###################################################################### |
| 311 | | | # |
| 312 | | | # fork into background |
| 313 | | | # |
| 314 | | | sub daemonize { |
| 315 | | | use POSIX qw(setsid); |
| 316 | | | HotSaNIClog::info("entering daemon mode..."); |
| 317 | | | if ($debug eq "") { |
| 318 | | | close STDIN; |
| 319 | | | close STDOUT; |
| 320 | | | close STDERR; |
| 321 | | | if (open(DEVTTY, "/dev/tty")) { ioctl(DEVTTY,0x20007471,0); close DEVTTY; } |
| 322 | | | open STDIN,"/dev/null"; |
| 323 | | | open STDOUT,">>".$CONFIG{LOGDIR}."/HotSaNIC.log"; |
| 324 | | | open STDERR,">&STDOUT"; |
| 325 | | | setpgrp(0,$$); |
| 326 | | | chdir "/"; |
| 327 | | | fork && exit 0; |
| 328 | | | setsid; |
| 329 | | | HotSaNIClog::info("archiver successfully forked into background and running on PID $$"); |
| 330 | | | } |
| 331 | | | else { HotSaNIClog::info("archiver running in debug-mode on PID $$"); } |
| 332 | | | HotSaNIClog::info("-" x 75); |
| 333 | | | open FILE,">".$CONFIG{PIDFILE}; |
| 334 | | | print FILE $$; |
| 335 | | | close FILE; |
| 336 | | | } |
| 337 | | | |
| 338 | | | |
| 339 | | | ###################################################################### |
| 340 | | | # |
| 341 | | | # rotate logfiles |
| 342 | | | # |
| 343 | | | sub logrotate { |
| 344 | | | my ($size,$file); |
| 345 | | | |
| 346 | | | if ($debug ne "d") { |
| 347 | | | |
| 348 | | | $file=$CONFIG{LOGDIR}."/HotSaNIC.log"; |
| 349 | | | (undef,undef,undef,undef,undef,undef,undef,$size,undef,undef,undef,undef,undef) = stat($file); |
| 350 | | | if (defined $size) { |
| 351 | | | if ($size > $CONFIG{LOGSIZE}) { |
| 352 | | | HotSaNIClog::info("$file exceeding $CONFIG{LOGSIZE} bytes - rotating - keeping $CONFIG{LOGBACKUPS} backups."); |
| 353 | | | for (my $nn=$CONFIG{LOGBACKUPS};$nn>1;$nn--) { |
| 354 | | | if ( -e "$file.$nn" ) { unlink "$file.$nn"; } |
| 355 | | | if ( -e "$file.".($nn-1) ) { rename "$file.".($nn-1),"$file.".$nn; } |
| 356 | | | } |
| 357 | | | close STDOUT; |
| 358 | | | rename $file,"$file.1"; |
| 359 | | | open STDOUT,">>$file"; |
| 360 | | | close STDERR; |
| 361 | | | rename $file,"$file.1"; |
| 362 | | | open STDERR,">>$file"; |
| 363 | | | } |
| 364 | | | } |
| 365 | | | } |
| 366 | | | } |
| 367 | | | |
| 368 | | | |
| 369 | | | ###################################################################### |
| 370 | | | # |
| 371 | | | # evaluate commandline arguments |
| 372 | | | # |
| 373 | | | sub check_for_args { |
| 374 | | | HotSaNIClog::info("evaluating cmdline arguments..."); |
| 375 | | | my @args=@_; |
| 376 | | | |
| 377 | | | # set initial values |
| 378 | | | # |
| 379 | | | my $debuglevel=0; |
| 380 | | | my $runmode=0; |
| 381 | | | |
| 382 | | | # Check if we were called with any options |
| 383 | | | # |
| 384 | | | foreach my $arg (@args) { |
| 385 | | | if (index($arg,"d") >= 0) { $runmode|=1; } |
| 386 | | | if (index($arg,"D") >= 0) { $runmode|=128; } |
| 387 | | | if (index($arg,"h") >= 0) { $runmode|=256; } |
| 388 | | | if (index($arg,"?") >= 0) { $runmode|=256; } |
| 389 | | | $arg=~ s/[a-zA-Z]//g; |
| 390 | | | if ($arg =~ /[0-9]/) { if ($arg > 0) { $debuglevel=$arg; } } |
| 391 | | | } |
| 392 | | | |
| 393 | | | if (($runmode == 0) || ($runmode > 255)) { |
| 394 | | | print "rrdtimer - CVS version $VERSION ($IDENTIFIER) - OS: $^O\n"; |
| 395 | | | print "usage:\nrrdtimer [options[debuglevel]]\n options:\n"; |
| 396 | | | print " D - enter daemon-mode (i.e. start the main loop)\n"; |
| 397 | | | print " d - enter debugging mode (don't fork into background)\n"; |
| 398 | | | print "\n"; |
| 399 | | | exit 0; |
| 400 | | | } |
| 401 | | | return ($debuglevel,$runmode); |
| 402 | | | } |
| 403 | | | |
| 404 | | | |