#!/usr/bin/perl -w use strict; use File::Basename ; use Proc::ProcessTable ; use lib '/usr/local/myNMS/bin' ; use My_Config ; use My_Utils ; my $VERSION='0.62' ; =pod Copyright (c) 2000-2001 John Stumbles and the University of Reading. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. 0.62 20Feb01 changed command logfile datestamp format from command.YYYYMMDD.log to command.log.YYYYMMDD 0.61.1 20Feb01 removed $CONFIG{DIR}{BASE} from myNMS subdir specs 0.61 15Feb01 KILL etc modes say what theyre doing on STDERR fixed error of prog starting up in normal mode if nothing to kill 0.6 15Feb01 -KILL|INT|HUP|SUSPEND modes kill/int or hups any running version, suspend mode also stays running so cron-launched version will not start (used to stop processes run by KeepAlive from a command line, without messing with KeepAlive config file or crontab) 0.51 15Feb01 startup message mentions version number logfiles are timestamped: YYYYMMDD.log 0.5 15Feb01 use My_Config catches SIG HUP and SIGINT and stops/restarts all commands (SIGHUP) or stops all commands and exits (SIGINT) 0.4.3 01Feb01 fixed bugette: printing not logging PIDs when restarting commands 0.4.2 24Jan01 path of bin dir prepended to commands unless full or relative path specified 0.4.1 24Jan01 added LOG function 0.4 23Jan01 accomodate new directory structure also change to get_pid() to change 'defunct' to 'zombie' for cross-OS portability 0.3 22Jan01 use Proc:ProcessTable for ps 0.2.2 22Jan01 cosmetic: write out our pid to stdout 0.2.1 22Jan01 added check for and kil defunct (zombie) proceses 0.2 20Jan01 fixed problem with ps not showing zombies =cut my %COMMAND ; # table of command names, their pids and whether they should be run or stopped my %PID ; # table of running pids my $BASENAME = basename($0, '.pl'); my $BIN_DIR = $CONFIG{DIR}{BIN} ; my $ETC_DIR = $CONFIG{DIR}{ETC} ; my $VAR_DIR = $CONFIG{DIR}{VAR} ; my $LOG_DIR = $CONFIG{DIR}{LOG} ; my $CONFIG_FILE = "$ETC_DIR/$BASENAME.cf"; my $PID_FILE = "$VAR_DIR/$BASENAME.pid" ; my $LOG_FILE = "$LOG_DIR/$BASENAME.log" ; sub LOG { open (LOG, ">>$LOG_FILE") or die "Could not open log file: $LOG_FILE - $!";print LOG (localtime(time). ': ', @_, "\n"); close LOG; } my $STOP = 0 ; my $EXIT = 0 ; $SIG{INT} = \&my_sig_handler; $SIG{HUP} = \&my_sig_handler; uc($ARGV[0]) =~ /-(KILL|HUP|INT|SUSPEND)/ ; # check cli arg exit unless startup($1) ; # check whether existing process is running LOG ("$0 V. $VERSION starting up") ; while(1) { # to take account of editing, deletion or commenting-out of commands in config file # get rid of non-running commads from table and set running commands' run state to off foreach my $cmd (keys %COMMAND) { if ($COMMAND{$cmd}{pid}) # is command currently running? { $COMMAND{$cmd}{run}=0 ; # set command's run state to 0 } else { delete $COMMAND{$cmd} ; # not running: delete command } } read_config() ; # (re)read config file (will re-read commands & their run states to whatever in file) get_pid() ; foreach my $cmd (keys %COMMAND) { # check for zombies: if ($COMMAND{$cmd}{pid}) # is/was this a running command? { if ($PID{$COMMAND{$cmd}{pid}}{state} eq 'zombie') # dead but pid still in table { LOG "command $cmd is zombie" ; my $wpid = waitpid $COMMAND{$cmd}{pid}, 0 ; $COMMAND{$cmd}{pid} = 0 ; } } if ($STOP or ! $COMMAND{$cmd}{run}) # should this command be run or stopped? { if ($COMMAND{$cmd}{pid}) # still have pid - is it still running? { if ($PID{$COMMAND{$cmd}{pid}}) { LOG "stopping $cmd" ; kill 9, $COMMAND{$cmd}{pid} ; my $wpid = waitpid $COMMAND{$cmd}{pid}, 0 ; $COMMAND{$cmd}{pid} = 0 ; } else { $COMMAND{$cmd}{pid} = 0 ; } } } else # run == true: start this command if not running { unless ($COMMAND{$cmd}{pid} and $PID{$COMMAND{$cmd}{pid}}) { run_command($COMMAND{$cmd}) ; LOG "starting $cmd - PID: $COMMAND{$cmd}{pid}" ; } } } if ($STOP) { LOG "all stopped" ; $STOP = 0 ; } if ($EXIT) { LOG "exiting" ; pid_file('delete') ; # delete pid file (if it is ours) last ; } sleep 10 ; } ############################################################################## sub my_sig_handler { my $sig = shift ; $SIG{$sig}=\&my_sig_handler; # if SysV - ptui! if ($sig eq 'INT') { LOG "SIGINT received: preparing to shut down"; $STOP=1; $EXIT=1; } elsif ($sig eq 'HUP') { LOG "SIGHUP received: stopping and restarting programs"; $STOP=1; } } ############################################################################## sub read_config { open (CFG, $CONFIG_FILE) or die "*** ERROR in $0 - could not open config file $CONFIG_FILE :- $!\n"; my $datestamp = YYYYMMDDhhmmss(undef, 8) ; # get YYYYMMDD while () { next if /^\s*(#.*)?$/; # next unless /^\s*([^#]*[^#\s])/; chomp ; my $CMD = $_ ; my ($cmd, $log, $errlog) = split /\t+/; next unless $cmd ; $COMMAND{$CMD}{full_cmd} = $cmd =~ m|^[$./]| ? $cmd : "$BIN_DIR/$cmd" ; # prepend path to our 'bin' dir unless full or relative path specified unless ($log) { $log = $cmd; # if logfile name not supplied take command name with spaces and /s replaced by underscores $log =~ s/\s+/_/g; $log =~ s|/|_|g; $log .= '.log' ; } # $log =~ s/(\.log)/".$datestamp$1"/ei ; # datestamp log file $log .= ".$datestamp" ; # datestamp log file $log = "$LOG_DIR/$log" unless $log =~ m|^[$./]| ; # prepend default log dir unless full or relative path to log file specified $COMMAND{$CMD}{log}=$log ; $errlog = $log unless $errlog ; $errlog = "$LOG_DIR/$errlog" unless $errlog =~ m|^[$./]| ; # prepend default log dir unless full or relative path to log file specified $COMMAND{$CMD}{errlog} = $errlog ; $COMMAND{$CMD}{full_cmd} =~ s/>.*// ; # remove any redirection # $COMMAND{$CMD}{cmd} = [split /\s+/, $COMMAND{$CMD}{full_cmd}] ; $COMMAND{$CMD}{full_cmd} .= " >> $log 2>$errlog"; $COMMAND{$CMD}{run} = 1 ; } close CFG ; } ############################################################################## sub get_pid { my $t = new Proc::ProcessTable; foreach my $p ( @{$t->table} ) { next unless $p->pid ; # skip PID 0 next if ($p->pid == $$); # skip our own PID $PID{$p->pid} = { state => (($p->state eq 'defunct') ? 'zombie' : $p->state), # un-Solaris-ise state for cross-OS portability cmdline => $p->cmndline } ; # record process info } } ############################################################################## sub run_command { my $cmd = shift or die "run_command called without command to run"; FORK: { if (my $pid = fork) { # parent $cmd->{pid} = $pid ; return ; } elsif (defined $pid) { exec $cmd->{full_cmd} ; # exec @{$cmd->{cmd}} ; exit ; } elsif ($! =~ /No more process/) { sleep 5 ; redo FORK ; } else { die "Can't fork: $!"; } } } ############################################################################## sub startup # check for existing process and write our pid to file { my $arg = (shift or '') ; my $pid = pid_file() ; # get existing pid from file if ($pid) # get existing pid from file { get_pid() ; if ($PID{$pid}) # original process still running { return 0 unless $arg ; if ($arg eq 'HUP' or $arg eq 'INT') { kill 1, $pid ; LOG "sending SIGHUP to running process ID $pid" ; print STDERR "sending SIGHUP to running process ID $pid\n" ; return 0 ; } elsif ($arg) # other args - all kill { kill 2, $pid ; LOG "sending SIGINT to running process ID $pid" ; print STDERR "sending SIGINT to running process ID $pid\n" ; return 0 unless ($arg eq 'SUSPEND') ; } } } return 0 if ($arg and $arg ne 'SUSPEND') ; # write our pid to file open (PID, ">$PID_FILE") or die "*** ERROR: in $0 unable to open to write to pid file $PID_FILE - $!\n" ; print "Writing our pid $$ to pid file $PID_FILE\n"; print PID $$ or die "*** ERROR: in $0 unable to write to pid file $PID_FILE - $!\n" ; close PID or die "*** ERROR: in $0 in writing pid to file $PID_FILE - $!\n" ; return 1 unless ($arg eq 'SUSPEND') ; LOG "*** stopped and suspended ***" ; print STDERR "*** Stopped and suspended\n" ; while (! $EXIT) # wait until we are killed by something else { sleep 10 ; } pid_file('delete') ; return 0 ; } ############################################################################## sub pid_file # check for existing pid file, if it exists return pid from it { my $delete = (shift or '') ; if (open (PID, $PID_FILE)) # my PID file exists { my $pid = ; # read pid value close PID ; return undef unless ($pid =~ /^\d+$/) ; # numeric value? if ($delete) { unlink $PID_FILE if $pid == $$ ; } return $pid ; } return 0 ; } ############################################################################## __END__