| #!/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); |
| } |
| |