📄 mtr_process.pl
字号:
# -*- cperl -*-# 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 Carp qw(cluck);use Socket;use Errno;use strict;#use POSIX ":sys_wait_h";use POSIX 'WNOHANG';sub mtr_run ($$$$$$;$);sub mtr_spawn ($$$$$$;$);sub mtr_stop_mysqld_servers ($);sub mtr_kill_leftovers ();sub mtr_record_dead_children ();sub mtr_exit ($);sub sleep_until_file_created ($$$);sub mtr_kill_processes ($);# static in Csub spawn_impl ($$$$$$$$);################################################################################ Execute an external command################################################################################ This function try to mimic the C version used in "netware/mysql_test_run.c"# FIXME learn it to handle append mode as well, a "new" flag or a "append"sub mtr_run ($$$$$$;$) { my $path= shift; my $arg_list_t= shift; my $input= shift; my $output= shift; my $error= shift; my $pid_file= shift; my $spawn_opts= shift; return spawn_impl($path,$arg_list_t,'run',$input,$output,$error,$pid_file, $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; my $spawn_opts= shift; return spawn_impl($path,$arg_list_t,'test',$input,$output,$error,$pid_file, $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; my $spawn_opts= shift; return spawn_impl($path,$arg_list_t,'spawn',$input,$output,$error,$pid_file, $spawn_opts);}################################################################################ If $join is set, we return the error code, else we return the PID###############################################################################sub spawn_impl ($$$$$$$$) { my $path= shift; my $arg_list_t= shift; my $mode= shift; my $input= shift; my $output= shift; my $error= shift; my $pid_file= shift; # FIXME my $spawn_opts= shift; if ( $::opt_script_debug ) { print STDERR "\n"; print STDERR "#### ", "-" x 78, "\n"; print STDERR "#### ", "STDIN $input\n" if $input; print STDERR "#### ", "STDOUT $output\n" if $output; print STDERR "#### ", "STDERR $error\n" if $error; print STDERR "#### ", "$mode : $path ", join(" ",@$arg_list_t), "\n"; print STDERR "#### ", "spawn options:\n"; if ($spawn_opts) { foreach my $key (sort keys %{$spawn_opts}) { print STDERR "#### ", " - $key: $spawn_opts->{$key}\n"; } } else { print STDERR "#### ", " none\n"; } print STDERR "#### ", "-" x 78, "\n"; } FORK: { my $pid= fork(); if ( ! defined $pid ) { if ( $! == $!{EAGAIN} ) # See "perldoc Errno" { mtr_debug("Got EAGAIN from fork(), sleep 1 second and redo"); sleep(1); redo FORK; } else { mtr_error("$path ($pid) can't be forked"); } } if ( $pid ) { spawn_parent_impl($pid,$mode,$path); } else { # Child, redirect output and exec # FIXME I tried POSIX::setsid() here to detach and, I hoped, # avoid zombies. But everything went wild, somehow the parent # became a deamon as well, and was hard to kill ;-) # Need to catch SIGCHLD and do waitpid or something instead...... $SIG{INT}= 'DEFAULT'; # Parent do some stuff, we don't if ( $::glob_cygwin_shell and $mode eq 'test' ) { # Programs started from mysqltest under Cygwin, are to # execute them within Cygwin. Else simple things in test # files like # --system "echo 1 > file" # will fail. # FIXME not working :-(# $ENV{'COMSPEC'}= "$::glob_cygwin_shell -c"; } my $log_file_open_mode = '>'; if ($spawn_opts and $spawn_opts->{'append_log_file'}) { $log_file_open_mode = '>>'; } if ( $output ) { if ( ! open(STDOUT,$log_file_open_mode,$output) ) { mtr_child_error("can't redirect STDOUT to \"$output\": $!"); } } if ( $error ) { if ( $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\": $!"); } } }}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, we wait for it to return my $ret_pid= waitpid($pid,0); if ( $ret_pid <= 0 ) { mtr_error("$path ($pid) got lost somehow"); } 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. # # FIXME is this as it should be? Can't mysqld terminate # normally from running a test case? 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; } # If one of the mysqld processes died, we want to # mark this, and kill the mysqltest process. foreach my $idx (0..1) { if ( $::master->[$idx]->{'pid'} eq $ret_pid ) { mtr_debug("child $ret_pid was master[$idx], " . "exit during mysqltest run"); $::master->[$idx]->{'pid'}= 0; last; } } foreach my $idx (0..2) { if ( $::slave->[$idx]->{'pid'} eq $ret_pid ) { mtr_debug("child $ret_pid was slave[$idx], " . "exit during mysqltest run"); $::slave->[$idx]->{'pid'}= 0; last; } } mtr_debug("waitpid() catched exit of unknown child $ret_pid, " . "exit during mysqltest run"); } if ( $ret_pid != $pid ) { # We terminated the waiting because a "mysqld" process died. # Kill the mysqltest process. kill(9,$pid); $ret_pid= waitpid($pid,0); if ( $ret_pid == -1 ) { 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################################################################################ We just "ping" on the ports, and if we can't do a socket connect# we assume the server is dead. So we don't *really* know a server# is dead, we just hope that it after letting the listen port go,# it is dead enough for us to start a new server.sub mtr_kill_leftovers () { # First, kill all masters and slaves that would conflict with # this run. Make sure to remove the PID file, if any. # FIXME kill IM manager first, else it will restart the servers, how?! my @args; for ( my $idx; $idx < 2; $idx++ ) { push(@args,{ pid => 0, # We don't know the PID pidfile => $::instance_manager->{'instances'}->[$idx]->{'path_pid'}, sockfile => $::instance_manager->{'instances'}->[$idx]->{'path_sock'}, port => $::instance_manager->{'instances'}->[$idx]->{'port'}, }); } for ( my $idx; $idx < 2; $idx++ ) { push(@args,{ pid => 0, # We don't know the PID pidfile => $::master->[$idx]->{'path_mypid'}, sockfile => $::master->[$idx]->{'path_mysock'}, port => $::master->[$idx]->{'path_myport'}, }); } for ( my $idx; $idx < 3; $idx++ ) { push(@args,{ pid => 0, # We don't know the PID pidfile => $::slave->[$idx]->{'path_mypid'}, sockfile => $::slave->[$idx]->{'path_mysock'}, port => $::slave->[$idx]->{'path_myport'}, }); } mtr_mysqladmin_shutdown(\@args, 20); # 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 # FIXME $path_run_dir or something my $rundir= "$::opt_vardir/run"; if ( -d $rundir ) { opendir(RUNDIR, $rundir) or mtr_error("can't open directory \"$rundir\": $!"); my @pids; while ( my $elem= readdir(RUNDIR) ) { my $pidfile= "$rundir/$elem"; if ( -f $pidfile ) { my $pid= mtr_get_pid_from_file($pidfile); # Race, could have been removed between I tested with -f # and the unlink() below, so I better check again with -f if ( ! unlink($pidfile) and -f $pidfile ) { mtr_error("can't remove $pidfile"); } if ( $::glob_cygwin_perl or kill(0, $pid) ) { push(@pids, $pid); # We know (cygwin guess) it exists } } } closedir(RUNDIR); if ( @pids ) { 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 {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -