summaryrefslogtreecommitdiff
path: root/local/fixproc
diff options
context:
space:
mode:
Diffstat (limited to 'local/fixproc')
-rwxr-xr-xlocal/fixproc694
1 files changed, 694 insertions, 0 deletions
diff --git a/local/fixproc b/local/fixproc
new file mode 100755
index 0000000..b79630b
--- /dev/null
+++ b/local/fixproc
@@ -0,0 +1,694 @@
+#!/usr/bin/perl
+#
+# fixproc [-min n] [-max n] [-check | -kill | -restart | -exist | -fix] proc ...
+#
+# fixproc exit code:
+# 0 ok
+# 1 check failed
+# 2 cannot restart
+# 3 cannot kill
+# 4 fix failed if fix is defined as kill or restart, then
+# cannot kill or cannot restart is return instead
+# 10 fixproc error
+#
+#
+# Fixes a process named "proc" by performing the specified action. The
+# actions can be check, kill, restart, exist, or fix. The action is specified
+# on the command line or is read from a default database, which describes
+# the default action to take for each process. The database format and
+# the meaning of each action are described below.
+#
+# database format
+# ---------------
+#
+# name foo required
+# cmd /a/b/name args required
+# min number optional, defaults to 1
+# max number optional, defaults to 1
+#
+# check {null, exist, shell} optional, defaults to exist if not defined
+# [shell command shell commands needed only if check=shell
+# ...
+# shell command
+# end_shell] keyword end_shell marks end of shell commands
+# fix {kill, restart, shell} required
+# [shell command shell commands needed only if fix=shell
+# ...
+# shell command
+# end_shell] keyword end_shell marks end of shell commands
+#
+# Blank lines and lines beginning with "#" are ignored.
+#
+#
+# Example:
+#
+# name test1
+# cmd nice /home/kong/z/test1 > /dev/null &
+# max 2
+# fix shell
+# xterm&
+# nice /home/kong/z/test1 > /dev/null &
+# end_shell
+#
+#
+# actions
+# -------
+# There are 5 possible actions: kill, restart, fix, exist, check. Fix is
+# defined to be the kill action, the restart action, or a series of shell
+# commands. Check is optionally defined in the database. If check is not
+# defined, it defaults to exist.
+#
+# If the action is specified on the cmd line, it is executed regardless of
+# check. The commands executed for each action type is as follow:
+#
+# switch action:
+# kill:
+# kill process, wait 5 seconds, kill -9 if still exist
+# if still exist
+# return "cannot kill"
+# else
+# return "ok"
+#
+# restart:
+# execute kill
+# if kill returned "cannot kill"
+# return "cannot kill"
+# restart by issuing cmd to shell
+# if check defined
+# execute check
+# if check succeeds
+# return "ok"
+# else
+# return "cannot restart"
+#
+# fix:
+# if fix=kill
+# execute kill
+# else if fix=restart
+# execute restart
+# else
+# execute shell commands
+# execute check
+#
+# check:
+# if check defined as null
+# return "fixproc error"
+# else
+# execute check
+# if check succeeds
+# return (execute exist)
+# return "check failed"
+#
+# exist:
+# if proc exists in ps && (min <= num. of processes <= max)
+# return "ok"
+# else
+# return "check failed"
+#
+#
+# If the action is not specified on the cmd line, the default action is the
+# fix action defined in the database. Fix is only executed if check fails:
+#
+# if fix defined
+# if check is not defined as null
+# execute check
+# if check succeeds
+# return "ok"
+# execute action defined for fix
+# else
+# return "fixproc error"
+#
+#
+# If proc is not specified on the command line, return "fixproc error."
+# Multiple proc's can be defined on the cmd line. When an error occurs
+# when multiple proc's are specified, the first error encountered halts the
+# script.
+#
+# For check shell scripts, any non-zero exit code means the check has failed.
+#
+#
+# Timothy Kong 3/1995
+
+use File::Temp qw(tempfile);
+
+$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 ($fh) = pop (@_);
+ local ($i) = pop (@_);
+
+ printf (STDERR "create_sh_script\n") if ($debug > 0);
+
+ $! = $fixproc_error;
+ while ( $shell_lines[$i] ne $shell_end_marker )
+ {
+ printf ($fh "%s", $shell_lines[$i]);
+ $i++;
+ }
+ close ($fh);
+ chmod 0755, $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 ($tmpfh, $tmpfile) = tempfile("fix_XXXXXXXX", DIR => "/tmp");
+
+ &create_sh_script ($fix{$proc}, $tmpfh, $tmpfile);
+
+ # return code is number divided by 256
+ $error_code = (system "$tmpfile") / 256;
+ unlink($tmpfile);
+ return ($fix_failed_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 ($tmpfh, $tmpfile) = tempfile("check_XXXXXXXX", DIR => "/tmp");
+
+ &create_sh_script ($fix{$proc}, $tmpfh, $tmpfile);
+
+ # return code is number divided by 256
+ $error_code = (system "$tmpfile") / 256;
+ unlink($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);
+ }
+