blob: 966bf6ad8ba200bb35a24a9aefc4b6384387c0be [file] [log] [blame]
#!/usr/local/bin/perl
#
# A generic fix script to take a conf file, an argument, and attempt a fix
# based on the actions in the config file. And, thats it for docs.
$database_file = '/local/etc/fixproc.conf';
$debug = 0; # specify debug level using -dN
# currently defined: -d1
$no_error = 0;
$check_failed_error = 1;
$cannot_restart_error = 2;
$cannot_kill_error = 3;
$cannot_fix_error = 4;
$fixproc_error = 10;
$min = 1;
$max = 1;
$cmd_line_action = '';
%min = ();
%max = ();
%cmd = ();
%check = ();
%fix = ();
$shell_lines = ();
@proc_list = ();
$shell_header = "#!/bin/sh\n";
$shell_end_marker = 'shell_end_marker';
&read_args();
&read_database();
# &dump_database(); # debug only
# change the default min. and max. number of processes allowed
if ($min != 1)
{
for $name ( keys (%min) )
{
$min{$name} = $min;
}
}
if ($max != 1)
{
for $name ( keys (%max) )
{
$max{$name} = $max;
}
}
# work on one process at a time
for $proc ( @proc_list )
{
$error_code = &work_on_proc ($proc);
############# uncomment next line when fully working ############
# exit $error_code if ($error_code);
die "error_code = $error_code\n" if ($error_code);
}
# create an executable shell script file
sub create_sh_script
{
local ($file) = pop (@_);
local ($i) = pop (@_);
printf (stderr "create_sh_script\n") if ($debug > 0);
$! = $fixproc_error;
open (file, ">"."$file") || die "$0: cannot open $file\n";
while ( $shell_lines[$i] ne $shell_end_marker )
{
printf (file "%s", $shell_lines[$i]);
$i++;
}
close (file);
system "chmod +x $file";
return file;
}
sub do_fix
{
local ($proc) = pop(@_);
printf (stderr "do_fix\n") if ($debug > 0);
if ($fix{$proc} eq '')
{
$! = $fixproc_error;
die "$0: internal error 4\n";
}
if ($fix{$proc} eq 'kill')
{
return &do_kill ($proc);
}
elsif ($fix{$proc} eq 'restart')
{
return &do_restart ($proc);
}
else
{
# it must be "shell", so execute the shell script defined in database
local ($tmpfile) = "/tmp/fix_$$";
&create_sh_script ($fix{$proc}, $tmpfile);
# return code is number divided by 256
$error_code = (system "$tmpfile") / 256;
system "rm $tmpfile";
return ($cannot_fix_error) if ($error_code != 0);
# sleep needed here?
return &do_exist ($proc);
}
}
sub do_check
{
local ($proc) = pop(@_);
printf (stderr "do_check\n") if ($debug > 0);
if ($check{$proc} eq '')
{
$! = $fixproc_error;
die "$0: internal error 2\n";
}
if ($check{$proc} ne 'exist')
{
# if not "exist", then it must be "shell", so execute the shell script
# defined in database
local ($tmpfile) = "/tmp/check_$$";
&create_sh_script ($check{$proc}, $tmpfile);
# return code is number divided by 256
$error_code = (system "$tmpfile") / 256;
system "rm $tmpfile";
return ($check_failed_error) if ($error_code != 0);
# check passed, continue
}
return &do_exist ($proc);
}
sub do_exist
{
local ($proc) = pop(@_);
printf (stderr "do_exist\n") if ($debug > 0);
# do ps, check to see if min <= no. of processes <= max
$! = $fixproc_error;
open (command, "/bin/ps -e | /bin/grep $proc | /bin/wc -l |")
|| die "$0: can't run ps-grep-wc command\n";
$proc_count = <command>;
if (($proc_count < $min{$proc}) || ($proc_count > $max{$proc}))
{
return $check_failed_error;
}
return $no_error;
}
sub do_kill
{
local ($proc) = pop(@_);
local ($second_kill_needed);
printf (stderr "do_kill\n") if ($debug > 0);
# first try kill
$! = $fixproc_error;
open (command, "/bin/ps -e | /bin/grep $proc |")
|| die "$0: can't run ps-grep-awk command\n";
while (<command>)
{
# match the first field of ps -e
$! = $fixproc_error;
/^\s*(\d+)\s/ || die "$0: can't match ps -e output\n";
system "kill $1";
}
# if process still exist, try kill -9
sleep 2;
$! = $fixproc_error;
open (command, "/bin/ps -e | /bin/grep $proc |")
|| die "$0: can't run ps-grep-awk command\n";
$second_kill_needed = 0;
while (<command>)
{
# match the first field of ps -e
$! = $fixproc_error;
/^\s*(\d+)\s/ || die "$0: can't match ps -e output\n";
system "kill -9 $1";
$second_kill_needed = 1;
}
return ($no_error) if ($second_kill_needed == 0);
# see if kill -9 worked
sleep 2;
$! = $fixproc_error;
open (command, "/bin/ps -e | /bin/grep $proc |")
|| die "$0: can't run ps-grep-awk command\n";
while (<command>)
{ # a process still exist, return error
return $cannot_kill_error;
}
return $no_error; # good, all dead
}
sub do_restart
{
local ($proc) = pop(@_);
local ($error_code);
printf (stderr "do_restart\n") if ($debug > 0);
$error_code = &do_kill ($proc);
return $error_code if ($error_code != $no_error);
die "$0: internal error 3\n" if ($cmd{$proc} eq '');
system "$cmd{$proc}";
# sleep needed here?
if ($check{$proc} ne 'null')
{
return $no_error if (&do_check($proc) == $no_error);
return $cannot_restart_error;
}
}
sub work_on_proc
{
local ($proc) = pop(@_);
local ($error_code);
printf (stderr "work_on_proc\n") if ($debug > 0);
if ($cmd_line_action eq '')
{
# perform action from database
if ($check{$proc} ne 'null')
{
$error_code = &do_check ($proc);
if ($error_code != $check_failed_error)
{
return $error_code;
}
}
return &do_fix ($proc);
}
else
{
# perform action from command line
$error_code = $no_error;
if ($cmd_line_action eq 'kill')
{
$error_code = &do_kill ($proc);
}
elsif ($cmd_line_action eq 'restart')
{
$error_code = &do_restart ($proc);
}
elsif ($cmd_line_action eq 'fix')
{
$error_code = &do_fix ($proc);
}
elsif ($cmd_line_action eq 'check')
{
if ( $check{$proc} eq 'null' )
{
exit $fixproc_error;
}
$error_code = &do_check ($proc);
}
elsif ($cmd_line_action eq 'exist')
{
$error_code = &do_exist ($proc);
}
else
{
$! = $fixproc_error;
die "$0: internal error 1\n";
}
}
}
sub dump_database
{
local ($name);
for $name (keys(%cmd))
{
printf ("name\t%s\n", $name);
printf ("cmd\t%s\n", $cmd{$name});
printf ("min\t%s\n", $min{$name});
printf ("max\t%s\n", $max{$name});
if ( $check{$name} =~ /[0-9]+/ )
{
printf ("check\tshell\n");
$i = $check{$name};
while ( $shell_lines[$i] ne $shell_end_marker )
{
printf ("%s", $shell_lines[$i]);
$i++;
}
}
else
{
printf ("check\t%s\n", $check{$name});
}
if ( $fix{$name} =~ /[0-9]+/ )
{
printf ("fix\tshell\n");
$i = $fix{$name};
while ( $shell_lines[$i] ne $shell_end_marker )
{
printf ("%s", $shell_lines[$i]);
$i++;
}
}
else
{
printf ("fix\t%s\n", $fix{$name});
}
printf ("\n");
}
}
sub read_database
{
local ($in_check_shell_lines) = 0;
local ($in_fix_shell_lines) = 0;
local ($name) = '';
local ($str1);
local ($str2);
$! = $fixproc_error;
open (db, $database_file) || die 'cannot open database file $database_file\n';
while (<db>)
{
if ((! /\S/) || (/^[ \t]*#.*$/))
{
# ignore blank lines or lines beginning with "#"
}
elsif ($in_check_shell_lines)
{
if ( /^\s*end_shell\s*$/ )
{
$in_check_shell_lines = 0;
push (@shell_lines, $shell_end_marker);
}
else
{
push (@shell_lines, $_);
}
}
elsif ($in_fix_shell_lines)
{
if ( /^\s*end_shell\s*$/ )
{
$in_fix_shell_lines = 0;
push (@shell_lines, $shell_end_marker);
}
else
{
push (@shell_lines, $_);
}
}
else
{
if ( ! /^\s*(\S+)\s+(\S.*)\s*$/ )
{
$! = $fixproc_error;
die "$0: syntax error in database\n$_";
}
$str1 = $1;
$str2 = $2;
if ($str1 eq 'name')
{
&finish_db_entry($name);
$name = $str2;
}
elsif ($str1 eq 'cmd')
{
$! = $fixproc_error;
die "$0: cmd specified before name in database\n$_\n"
if ($name eq '');
die "$0: cmd specified multiple times for $name in database\n"
if ($cmd{$name} ne '');
$cmd{$name} = $str2;
}
elsif ($str1 eq 'min')
{
$! = $fixproc_error;
die "$0: min specified before name in database\n$_\n"
if ($name eq '');
die "$0: min specified multiple times in database\n$_\n"
if ($min{$name} ne '');
die "$0: non-numeric min value in database\n$_\n"
if ( ! ($str2 =~ /[0-9]+/ ));
$min{$name} = $str2;
}
elsif ($str1 eq 'max')
{
$! = $fixproc_error;
die "$0: max specified before name in database\n$_\n"
if ($name eq '');
die "$0: max specified multiple times in database\n$_\n"
if ($max{$name} ne '');
die "$0: non-numeric max value in database\n$_\n"
if ( ! ($str2 =~ /[0-9]+/ ));
$max{$name} = $str2;
}
elsif ($str1 eq 'check')
{
$! = $fixproc_error;
die "$0: check specified before name in database\n$_\n"
if ($name eq '');
die "$0: check specified multiple times in database\n$_\n"
if ($check{$name} ne '');
if ( $str2 eq 'shell' )
{
# if $check{$name} is a number, it is a pointer into
# $shell_lines[] where the shell commands are kept
$shell_lines[$#shell_lines+1] = $shell_header;
$check{$name} = $#shell_lines;
$in_check_shell_lines = 1;
}
else
{
$check{$name} = $str2;
}
}
elsif ($str1 eq 'fix')
{
$! = $fixproc_error;
die "$0: fix specified before name in database\n$_\n"
if ($name eq '');
die "$0: fix specified multiple times in database\n$_\n"
if ($fix{$name} ne '');
if ( $str2 eq 'shell' )
{
# if $fix{$name} is a number, it is a pointer into
# $shell_lines[] where the shell commands are kept
$shell_lines[$#shell_lines+1] = $shell_header;
$fix{$name} = $#shell_lines;
$in_fix_shell_lines = 1;
}
else
{
$fix{$name} = $str2;
}
}
}
}
&finish_db_entry($name);
}
sub finish_db_entry
{
local ($name) = pop(@_);
if ($name ne '')
{
$! = $fixproc_error;
die "$0: fix not defined for $name in database\n"
if ($fix{$name} eq '');
die "$0: cmd not defined for $name in database\n"
if ($cmd{$name} eq '');
$check{$name} = 'exist' if ($check{$name} eq '');
$max{$name} = 1 if ($max{$name} eq '');
$min{$name} = 1 if ($min{$name} eq '');
}
}
sub read_args
{
local ($i) = 0;
local ($arg);
local ($action_arg_count) = 0;
while ( $i <= $#ARGV )
{
$arg = $ARGV[$i];
if (($arg eq '-min') || ($arg eq '-max'))
{
if (($i == $#ARGV - 1) || ($ARGV[$i+1] =~ /\D/)) # \D is non-numeric
{
$! = $fixproc_error;
die "$0: numeric arg missing after -min or -max\n";
}
if ($arg eq '-min')
{
$min = $ARGV[$i+1];
}
else
{
$max = $ARGV[$i+1];
}
$i += 2;
}
elsif ($arg eq '-kill')
{
$cmd_line_action = 'kill';
$action_arg_count++;
$i++;
}
elsif ($arg eq '-check')
{
$cmd_line_action = 'check';
$action_arg_count++;
$i++;
}
elsif ($arg eq '-restart')
{
$cmd_line_action = 'restart';
$action_arg_count++;
$i++;
}
elsif ($arg eq '-exist')
{
$cmd_line_action = 'exist';
$action_arg_count++;
$i++;
}
elsif ($arg eq '-fix')
{
$cmd_line_action = 'fix';
$action_arg_count++;
$i++;
}
elsif ($arg =~ /-d(\d)$/)
{
$debug = $1;
$i++;
}
elsif ($arg =~ /^-/)
{
$! = $fixproc_error;
die "$0: unknown switch $arg\n";
}
else
{
push (@proc_list, $arg);
$i++;
}
}
$! = $fixproc_error;
die "$0: no process specified\n" if ($#proc_list == -1);
die "$0: more than one action specified\n" if ($action_arg_count > 1);
}