and added files
[bcm963xx.git] / userapps / opensource / net-snmp / local / fixproc
diff --git a/userapps/opensource/net-snmp/local/fixproc b/userapps/opensource/net-snmp/local/fixproc
new file mode 100755 (executable)
index 0000000..d6021bf
--- /dev/null
@@ -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
+
+$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 ($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 ($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);
+  }
+