209 lines
5.5 KiB
Perl
209 lines
5.5 KiB
Perl
# -*- cperl -*-
|
|
# Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License, version 2.0,
|
|
# as published by the Free Software Foundation.
|
|
#
|
|
# This program is also distributed with certain software (including
|
|
# but not limited to OpenSSL) that is licensed under separate terms,
|
|
# as designated in a particular file or component or in included license
|
|
# documentation. The authors of MySQL hereby grant you an additional
|
|
# permission to link the program and your derivative works with the
|
|
# separately licensed software that they have included with MySQL.
|
|
#
|
|
# 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, version 2.0, 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.
|
|
|
|
#
|
|
# Utility functions for Process management
|
|
#
|
|
|
|
package My::SafeProcess::Base;
|
|
|
|
use strict;
|
|
|
|
use Carp;
|
|
use IO::Pipe;
|
|
|
|
use base qw(Exporter);
|
|
our @EXPORT = qw(create_process);
|
|
|
|
# threads.pm may not exist everywhere, so use only on Windows.
|
|
use if $^O eq "MSWin32", "threads";
|
|
use if $^O eq "MSWin32", "threads::shared";
|
|
my $win32_spawn_lock : shared;
|
|
|
|
# Retry a couple of times if fork returns EAGAIN
|
|
sub _safe_fork {
|
|
my $retries = 5;
|
|
my $pid;
|
|
|
|
FORK:
|
|
{
|
|
$pid = fork;
|
|
if (not defined($pid)) {
|
|
croak("fork failed after: $!") if (!$retries--);
|
|
|
|
warn("fork failed sleep 1 second and redo: $!");
|
|
sleep(1);
|
|
redo FORK;
|
|
}
|
|
}
|
|
|
|
return $pid;
|
|
}
|
|
|
|
# Decode exit status
|
|
sub exit_status {
|
|
my $self = shift;
|
|
my $raw = $self->{EXIT_STATUS};
|
|
|
|
croak("Can't call exit_status before process has died")
|
|
unless defined $raw;
|
|
|
|
if ($raw & 127) {
|
|
# Killed by signal
|
|
my $signal_num = $raw & 127;
|
|
my $dumped_core = $raw & 128;
|
|
return 1; # Return error code
|
|
} else {
|
|
# Normal process exit
|
|
return $raw >> 8;
|
|
}
|
|
}
|
|
|
|
# Create a new process and return pid of the new process.
|
|
sub create_process {
|
|
my %opts = (@_);
|
|
|
|
my $args = delete($opts{'args'}) or die "args required";
|
|
my $error = delete($opts{'error'});
|
|
my $input = delete($opts{'input'});
|
|
my $open_mode = $opts{append} ? ">>" : ">";
|
|
my $output = delete($opts{'output'});
|
|
my $path = delete($opts{'path'}) or die "path required";
|
|
my $pid_file = delete($opts{'pid_file'});
|
|
|
|
if ($^O eq "MSWin32") {
|
|
lock($win32_spawn_lock);
|
|
|
|
# Input output redirect
|
|
my ($oldin, $oldout, $olderr);
|
|
open $oldin, '<&', \*STDIN or die "Failed to save old stdin: $!";
|
|
open $oldout, '>&', \*STDOUT or die "Failed to save old stdout: $!";
|
|
open $olderr, '>&', \*STDERR or die "Failed to save old stderr: $!";
|
|
|
|
if ($input) {
|
|
if (!open(STDIN, "<", $input)) {
|
|
croak("can't redirect STDIN to '$input': $!");
|
|
}
|
|
}
|
|
|
|
if ($output) {
|
|
if (!open(STDOUT, $open_mode, $output)) {
|
|
croak("can't redirect STDOUT to '$output': $!");
|
|
}
|
|
}
|
|
|
|
if ($error) {
|
|
if ($output eq $error) {
|
|
if (!open(STDERR, ">&STDOUT")) {
|
|
croak("can't dup STDOUT: $!");
|
|
}
|
|
} elsif (!open(STDERR, $open_mode, $error)) {
|
|
croak("can't redirect STDERR to '$error': $!");
|
|
}
|
|
}
|
|
|
|
# Magic use of 'system(1, @args)' to spawn a process
|
|
# and get a proper Win32 pid.
|
|
unshift(@$args, $path);
|
|
my $pid = system(1, @$args);
|
|
if ($pid == 0) {
|
|
print $olderr "create_process failed: $^E\n";
|
|
die "create_process failed: $^E";
|
|
}
|
|
|
|
# Retore IO redirects
|
|
open STDERR, '>&', $olderr or
|
|
croak("unable to reestablish STDERR");
|
|
open STDOUT, '>&', $oldout or
|
|
croak("unable to reestablish STDOUT");
|
|
open STDIN, '<&', $oldin or
|
|
croak("unable to reestablish STDIN");
|
|
|
|
return $pid;
|
|
}
|
|
|
|
local $SIG{PIPE} = sub { print STDERR "Got signal $@\n"; };
|
|
my $pipe = IO::Pipe->new();
|
|
my $pid = _safe_fork();
|
|
|
|
if ($pid) {
|
|
# Parent process
|
|
$pipe->reader();
|
|
|
|
# Wait for child to say it's ready
|
|
my $line = <$pipe>;
|
|
|
|
# If pid-file is defined, read process id from pid-file.
|
|
if (defined $pid_file) {
|
|
sleep 1 until -e $pid_file;
|
|
open FILE, $pid_file;
|
|
chomp(my $pid_val = <FILE>);
|
|
close FILE;
|
|
return $pid_val;
|
|
}
|
|
return $pid;
|
|
}
|
|
|
|
$SIG{INT} = 'DEFAULT';
|
|
|
|
# Make this process it's own process group to be able to kill
|
|
# it and any childs(that hasn't changed group themself).
|
|
setpgrp(0, 0) if $opts{setpgrp};
|
|
|
|
if ($output and !open(STDOUT, $open_mode, $output)) {
|
|
croak("can't redirect STDOUT to '$output': $!");
|
|
}
|
|
|
|
if ($error) {
|
|
if (defined $output and $output eq $error) {
|
|
if (!open(STDERR, ">&STDOUT")) {
|
|
croak("can't dup STDOUT: $!");
|
|
}
|
|
} elsif (!open(STDERR, $open_mode, $error)) {
|
|
croak("can't redirect STDERR to '$error': $!");
|
|
}
|
|
}
|
|
|
|
if ($input) {
|
|
if (!open(STDIN, "<", $input)) {
|
|
croak("can't redirect STDIN to '$input': $!");
|
|
}
|
|
}
|
|
|
|
if (!exec($path, @$args)) {
|
|
croak("Failed to exec '$path': $!");
|
|
}
|
|
|
|
# Tell parent to continue
|
|
$pipe->writer();
|
|
print $pipe "ready\n";
|
|
|
|
croak("Should never come here");
|
|
}
|
|
|
|
1;
|