mtr_process.pl

来自「视频监控网络部分的协议ddns,的模块的实现代码,请大家大胆指正.」· PL 代码 · 共 1,141 行 · 第 1/2 页

PL
1,141
字号
# -*- cperl -*-# Copyright (C) 2004-2006 MySQL AB# # This program is free software; you can redistribute it and/or modify# it under the terms of the GNU General Public License as published by# the Free Software Foundation; version 2 of the License.# # This program is distributed in the hope that it will be useful,# but WITHOUT ANY WARRANTY; without even the implied warranty of# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the# GNU General Public License for more details.# # You should have received a copy of the GNU General Public License# along with this program; if not, write to the Free Software# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA# This is a library file used by the Perl version of mysql-test-run,# and is part of the translation of the Bourne shell script with the# same name.use Socket;use Errno;use strict;use POSIX qw(WNOHANG SIGHUP);sub mtr_run ($$$$$$;$);sub mtr_spawn ($$$$$$;$);sub mtr_check_stop_servers ($);sub mtr_kill_leftovers ();sub mtr_wait_blocking ($);sub mtr_record_dead_children ();sub mtr_ndbmgm_start($$);sub mtr_mysqladmin_start($$$);sub mtr_exit ($);sub sleep_until_file_created ($$$);sub mtr_kill_processes ($);sub mtr_ping_with_timeout($);sub mtr_ping_port ($);# Local functionsub spawn_impl ($$$$$$$);################################################################################  Execute an external command###############################################################################sub mtr_run ($$$$$$;$) {  my $path=       shift;  my $arg_list_t= shift;  my $input=      shift;  my $output=     shift;  my $error=      shift;  my $pid_file=   shift; # Not used  my $spawn_opts= shift;  return spawn_impl($path,$arg_list_t,'run',$input,$output,$error,    $spawn_opts);}sub mtr_run_test ($$$$$$;$) {  my $path=       shift;  my $arg_list_t= shift;  my $input=      shift;  my $output=     shift;  my $error=      shift;  my $pid_file=   shift; # Not used  my $spawn_opts= shift;  return spawn_impl($path,$arg_list_t,'test',$input,$output,$error,    $spawn_opts);}sub mtr_spawn ($$$$$$;$) {  my $path=       shift;  my $arg_list_t= shift;  my $input=      shift;  my $output=     shift;  my $error=      shift;  my $pid_file=   shift; # Not used  my $spawn_opts= shift;  return spawn_impl($path,$arg_list_t,'spawn',$input,$output,$error,    $spawn_opts);}sub spawn_impl ($$$$$$$) {  my $path=       shift;  my $arg_list_t= shift;  my $mode=       shift;  my $input=      shift;  my $output=     shift;  my $error=      shift;  my $spawn_opts= shift;  if ( $::opt_script_debug )  {    mtr_report("");    mtr_debug("-" x 73);    mtr_debug("STDIN  $input") if $input;    mtr_debug("STDOUT $output") if $output;    mtr_debug("STDERR $error") if $error;    mtr_debug("$mode: $path ", join(" ",@$arg_list_t));    mtr_debug("spawn options:");    if ($spawn_opts)    {      foreach my $key (sort keys %{$spawn_opts})      {        mtr_debug("  - $key: $spawn_opts->{$key}");      }    }    else    {      mtr_debug("  none");    }    mtr_debug("-" x 73);    mtr_report("");  }  mtr_error("Can't spawn with empty \"path\"") unless defined $path; FORK:  {    my $pid= fork();    if ( ! defined $pid )    {      if ( $! == $!{EAGAIN} )           # See "perldoc Errno"      {        mtr_warning("Got EAGAIN from fork(), sleep 1 second and redo");        sleep(1);        redo FORK;      }      mtr_error("$path ($pid) can't be forked, error: $!");    }    if ( $pid )    {      select(STDOUT) if $::glob_win32_perl;      return spawn_parent_impl($pid,$mode,$path);    }    else    {      # Child, redirect output and exec      $SIG{INT}= 'DEFAULT';         # Parent do some stuff, we don't      my $log_file_open_mode = '>';      if ($spawn_opts and $spawn_opts->{'append_log_file'})      {        $log_file_open_mode = '>>';      }      if ( $output )      {	if ( $::glob_win32_perl )	{	  # Don't redirect stdout on ActiveState perl since this is          # just another thread in the same process.	}        elsif ( ! open(STDOUT,$log_file_open_mode,$output) )        {          mtr_child_error("can't redirect STDOUT to \"$output\": $!");        }      }      if ( $error )      {        if ( !$::glob_win32_perl and $output eq $error )        {          if ( ! open(STDERR,">&STDOUT") )          {            mtr_child_error("can't dup STDOUT: $!");          }        }        else        {          if ( ! open(STDERR,$log_file_open_mode,$error) )          {            mtr_child_error("can't redirect STDERR to \"$error\": $!");          }        }      }      if ( $input )      {        if ( ! open(STDIN,"<",$input) )        {          mtr_child_error("can't redirect STDIN to \"$input\": $!");        }      }      if ( ! exec($path,@$arg_list_t) )      {        mtr_child_error("failed to execute \"$path\": $!");      }      mtr_error("Should never come here 1!");    }    mtr_error("Should never come here 2!");  }  mtr_error("Should never come here 3!");}sub spawn_parent_impl {  my $pid=  shift;  my $mode= shift;  my $path= shift;  if ( $mode eq 'run' or $mode eq 'test' )  {    if ( $mode eq 'run' )    {      # Simple run of command, wait blocking for it to return      my $ret_pid= waitpid($pid,0);      if ( $ret_pid != $pid )      {	# The "simple" waitpid has failed, print debug info	# and try to handle the error        mtr_warning("waitpid($pid, 0) returned $ret_pid " .		    "when waiting for '$path', error: '$!'");	if ( $ret_pid == -1 )	{	  # waitpid returned -1, that would indicate the process	  # no longer exist and waitpid couldn't wait for it.	  return 1;	}	mtr_error("Error handling failed");      }      return mtr_process_exit_status($?);    }    else    {      # We run mysqltest and wait for it to return. But we try to      # catch dying mysqld processes as well.      #      # We do blocking waitpid() until we get the return from the      # "mysqltest" call. But if a mysqld process dies that we      # started, we take this as an error, and kill mysqltest.      my $exit_value= -1;      my $saved_exit_value;      my $ret_pid;                      # What waitpid() returns      while ( ($ret_pid= waitpid(-1,0)) != -1 )      {        # Someone terminated, don't know who. Collect        # status info first before $? is lost,        # but not $exit_value, this is flagged from        my $timer_name= mtr_timer_timeout($::glob_timers, $ret_pid);        if ( $timer_name )        {          if ( $timer_name eq "suite" )          {            # We give up here            # FIXME we should only give up the suite, not all of the run?            print STDERR "\n";            mtr_error("Test suite timeout");          }          elsif ( $timer_name eq "testcase" )          {            $saved_exit_value=  63;       # Mark as timeout            kill(9, $pid);                # Kill mysqltest            next;                         # Go on and catch the termination          }        }        if ( $ret_pid == $pid )        {          # We got termination of mysqltest, we are done          $exit_value= mtr_process_exit_status($?);          last;        }        # One of the child processes died, unless this was expected	# mysqltest should be killed and test aborted	check_expected_crash_and_restart($ret_pid);      }      if ( $ret_pid != $pid )      {        # We terminated the waiting because a "mysqld" process died.        # Kill the mysqltest process.	mtr_verbose("Kill mysqltest because another process died");        kill(9,$pid);        $ret_pid= waitpid($pid,0);        if ( $ret_pid != $pid )        {          mtr_error("$path ($pid) got lost somehow");        }      }      return $saved_exit_value || $exit_value;    }  }  else  {    # We spawned a process we don't wait for    return $pid;  }}# ----------------------------------------------------------------------# We try to emulate how an Unix shell calculates the exit code# ----------------------------------------------------------------------sub mtr_process_exit_status {  my $raw_status= shift;  if ( $raw_status & 127 )  {    return ($raw_status & 127) + 128;  # Signal num + 128  }  else  {    return $raw_status >> 8;           # Exit code  }}################################################################################  Kill processes left from previous runs################################################################################ Kill all processes(mysqld, ndbd, ndb_mgmd and im) that would conflict with# this run# Make sure to remove the PID file, if any.# kill IM manager first, else it will restart the serverssub mtr_kill_leftovers () {  mtr_report("Killing Possible Leftover Processes");  mtr_debug("mtr_kill_leftovers(): started.");  my @kill_pids;  my %admin_pids;  foreach my $srv (@{$::master}, @{$::slave})  {    mtr_debug("  - mysqld " .              "(pid: $srv->{pid}; " .              "pid file: '$srv->{path_pid}'; " .              "socket: '$srv->{path_sock}'; ".              "port: $srv->{port})");    my $pid= mtr_mysqladmin_start($srv, "shutdown", 70);    # Save the pid of the mysqladmin process    $admin_pids{$pid}= 1;    push(@kill_pids,{		     pid      => $srv->{'pid'},		     pidfile  => $srv->{'path_pid'},		     sockfile => $srv->{'path_sock'},		     port     => $srv->{'port'},		    });    $srv->{'pid'}= 0; # Assume we are done with it  }  if ( ! $::opt_skip_ndbcluster )  {    # Start shutdown of clusters.    mtr_debug("Shutting down cluster...");    foreach my $cluster (@{$::clusters})    {      mtr_debug("  - cluster " .		"(pid: $cluster->{pid}; " .		"pid file: '$cluster->{path_pid})");      my $pid= mtr_ndbmgm_start($cluster, "shutdown");      # Save the pid of the ndb_mgm process      $admin_pids{$pid}= 1;      push(@kill_pids,{		       pid      => $cluster->{'pid'},		       pidfile  => $cluster->{'path_pid'}		      });      $cluster->{'pid'}= 0; # Assume we are done with it      foreach my $ndbd (@{$cluster->{'ndbds'}})      {	mtr_debug("    - ndbd " .		  "(pid: $ndbd->{pid}; " .		  "pid file: '$ndbd->{path_pid})");	push(@kill_pids,{			 pid      => $ndbd->{'pid'},			 pidfile  => $ndbd->{'path_pid'},			});	$ndbd->{'pid'}= 0; # Assume we are done with it      }    }  }  # Wait for all the admin processes to complete  mtr_wait_blocking(\%admin_pids);  # If we trusted "mysqladmin --shutdown_timeout= ..." we could just  # terminate now, but we don't (FIXME should be debugged).  # So we try again to ping and at least wait the same amount of time  # mysqladmin would for all to die.  mtr_ping_with_timeout(\@kill_pids);  # We now have tried to terminate nice. We have waited for the listen  # port to be free, but can't really tell if the mysqld process died  # or not. We now try to find the process PID from the PID file, and  # send a kill to that process. Note that Perl let kill(0,@pids) be  # a way to just return the numer of processes the kernel can send  # signals to. So this can be used (except on Cygwin) to determine  # if there are processes left running that we cound out might exists.  #  # But still after all this work, all we know is that we have  # the ports free.  # We scan the "var/run/" directory for other process id's to kill  my $rundir= "$::opt_vardir/run";  mtr_debug("Processing PID files in directory '$rundir'...");  if ( -d $rundir )  {    opendir(RUNDIR, $rundir)      or mtr_error("can't open directory \"$rundir\": $!");    my @pids;    while ( my $elem= readdir(RUNDIR) )    {      # Only read pid from files that end with .pid      if ( $elem =~ /.*[.]pid$/)      {	my $pidfile= "$rundir/$elem";	if ( -f $pidfile )	{	  mtr_debug("Processing PID file: '$pidfile'...");	  my $pid= mtr_get_pid_from_file($pidfile);	  mtr_debug("Got pid: $pid from file '$pidfile'");	  if ( $::glob_cygwin_perl or kill(0, $pid) )	  {	    mtr_debug("There is process with pid $pid -- scheduling for kill.");	    push(@pids, $pid);            # We know (cygwin guess) it exists	  }	  else	  {	    mtr_debug("There is no process with pid $pid -- skipping.");	  }	}      }      else      {	mtr_warning("Found non pid file $elem in $rundir")	  if -f "$rundir/$elem";	next;      }    }    closedir(RUNDIR);    if ( @pids )    {      mtr_debug("Killing the following processes with PID files: " .                join(' ', @pids) . "...");      start_reap_all();      if ( $::glob_cygwin_perl )      {        # We have no (easy) way of knowing the Cygwin controlling        # process, in the PID file we only have the Windows process id.        system("kill -f " . join(" ",@pids)); # Hope for the best....        mtr_debug("Sleep 5 seconds waiting for processes to die");        sleep(5);      }      else      {        my $retries= 10;                    # 10 seconds        do        {          mtr_debug("Sending SIGKILL to pids: " . join(' ', @pids));          kill(9, @pids);          mtr_report("Sleep 1 second waiting for processes to die");          sleep(1)                      # Wait one second        } while ( $retries-- and  kill(0, @pids) );        if ( kill(0, @pids) )           # Check if some left        {          mtr_warning("can't kill process(es) " . join(" ", @pids));        }      }      stop_reap_all();    }  }  else  {    mtr_debug("Directory for PID files ($rundir) does not exist.");  }  # We may have failed everything, but we now check again if we have  # the listen ports free to use, and if they are free, just go for it.  mtr_debug("Checking known mysqld servers...");  foreach my $srv ( @kill_pids )  {    if ( defined $srv->{'port'} and mtr_ping_port($srv->{'port'}) )    {      mtr_warning("can't kill old process holding port $srv->{'port'}");    }  }  mtr_debug("mtr_kill_leftovers(): finished.");}## Check that all processes in "spec" are shutdown gracefully# else kill them off hard#sub mtr_check_stop_servers ($) {  my $spec=  shift;  # Return if no processes are defined  return if ! @$spec;  mtr_verbose("mtr_check_stop_servers");  # ----------------------------------------------------------------------  # Wait until servers in "spec" has stopped listening  # to their ports or timeout occurs  # ----------------------------------------------------------------------  mtr_ping_with_timeout(\@$spec);  # ----------------------------------------------------------------------  # Use waitpid() nonblocking for a little while, to see how  # many process's will exit sucessfully.  # This is the normal case.  # ----------------------------------------------------------------------  my $wait_counter= 50; # Max number of times to redo the loop  foreach my $srv ( @$spec )  {    my $pid= $srv->{'pid'};    my $ret_pid;    if ( $pid )    {      $ret_pid= waitpid($pid,&WNOHANG);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?