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 + -
显示快捷键?