зеркало из https://github.com/mozilla/gecko-dev.git
Bug 372582: RunShellCommand() does not reap timed-out children; also, if output is 1, the output really should print immediately, not be buffered. r=rhelmer
This commit is contained in:
Родитель
4573b68c1a
Коммит
41b40d8ee7
|
@ -15,6 +15,7 @@ our @EXPORT_OK = qw(RunShellCommand MkdirWithPath HashFile DownloadFile Email);
|
||||||
|
|
||||||
my $DEFAULT_EXEC_TIMEOUT = 600;
|
my $DEFAULT_EXEC_TIMEOUT = 600;
|
||||||
my $EXEC_IO_READINCR = 1000;
|
my $EXEC_IO_READINCR = 1000;
|
||||||
|
my $EXEC_REAP_TIMEOUT = 10;
|
||||||
|
|
||||||
# RunShellCommand is a safe, performant way that handles all the gotchas of
|
# RunShellCommand is a safe, performant way that handles all the gotchas of
|
||||||
# spawning a simple shell command. It's meant to replace backticks and open()s,
|
# spawning a simple shell command. It's meant to replace backticks and open()s,
|
||||||
|
@ -101,8 +102,21 @@ sub RunShellCommand {
|
||||||
my $output = '';
|
my $output = '';
|
||||||
my $childPid = 0;
|
my $childPid = 0;
|
||||||
my $childStartedTime = 0;
|
my $childStartedTime = 0;
|
||||||
|
my $childReaped = 0;
|
||||||
|
my $prevStdoutBufferingSetting = 0;
|
||||||
local *LOGFILE;
|
local *LOGFILE;
|
||||||
|
|
||||||
|
if ($printOutputImmediately) {
|
||||||
|
# We Only end up doing this if it's requested that we're going to print
|
||||||
|
# output immediately. Additionally, we can't call autoflush() on STDOUT
|
||||||
|
# here, because doing so automatically sets it to on (gee, thanks);
|
||||||
|
# $| is the only way to get the value.
|
||||||
|
my $prevFd = select(STDOUT);
|
||||||
|
$prevStdoutBufferingSetting = $|;
|
||||||
|
select($prevFd);
|
||||||
|
STDOUT->autoflush(1);
|
||||||
|
}
|
||||||
|
|
||||||
if (defined($changeToDir)) {
|
if (defined($changeToDir)) {
|
||||||
chdir($changeToDir) or die("RunShellCommand(): failed to chdir() to "
|
chdir($changeToDir) or die("RunShellCommand(): failed to chdir() to "
|
||||||
. "$changeToDir\n");
|
. "$changeToDir\n");
|
||||||
|
@ -129,7 +143,14 @@ sub RunShellCommand {
|
||||||
if ($args{'background'}) {
|
if ($args{'background'}) {
|
||||||
alarm(0);
|
alarm(0);
|
||||||
|
|
||||||
|
# Restore external state
|
||||||
chdir($cwd) if (defined($changeToDir));
|
chdir($cwd) if (defined($changeToDir));
|
||||||
|
if ($printOutputImmediately) {
|
||||||
|
my $prevFd = select(STDOUT);
|
||||||
|
$| = $prevStdoutBufferingSetting;
|
||||||
|
select($prevFd);
|
||||||
|
}
|
||||||
|
|
||||||
return { startTime => $childStartedTime,
|
return { startTime => $childStartedTime,
|
||||||
endTime => undef,
|
endTime => undef,
|
||||||
timedOut => $timedOut,
|
timedOut => $timedOut,
|
||||||
|
@ -158,7 +179,6 @@ sub RunShellCommand {
|
||||||
# IF NOTHING ELSE, the alarm() we set will catch a program that
|
# IF NOTHING ELSE, the alarm() we set will catch a program that
|
||||||
# fails to finish executing within the timeout period.
|
# fails to finish executing within the timeout period.
|
||||||
|
|
||||||
my $childReaped = 0;
|
|
||||||
while (my @ready = $childSelect->can_read()) {
|
while (my @ready = $childSelect->can_read()) {
|
||||||
foreach my $fh (@ready) {
|
foreach my $fh (@ready) {
|
||||||
my $line = undef;
|
my $line = undef;
|
||||||
|
@ -212,15 +232,41 @@ sub RunShellCommand {
|
||||||
if ($@) {
|
if ($@) {
|
||||||
if ($@ eq "alarm\n") {
|
if ($@ eq "alarm\n") {
|
||||||
$timedOut = 1;
|
$timedOut = 1;
|
||||||
kill(9, $childPid) or die("Could not kill timed-out $childPid: $!");
|
if ($childReaped) {
|
||||||
warn "Shell command $shellCommand timed out, PID $childPid killed: $@\n";
|
die('ASSERT: RunShellCommand(): timed out, but child already '.
|
||||||
|
'reaped?');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kill('KILL', $childPid) != 1) {
|
||||||
|
warn("SIGKILL to timed-out child $childPid failed: $!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
# Processes get 10 seconds to obey.
|
||||||
|
eval {
|
||||||
|
local $SIG{'ALRM'} = sub { die("alarm\n") };
|
||||||
|
alarm($EXEC_REAP_TIMEOUT);
|
||||||
|
my $waitRv = waitpid($childPid, 0);
|
||||||
|
alarm(0);
|
||||||
|
# Don't fill in these values if they're bogus.
|
||||||
|
if ($waitRv > 0) {
|
||||||
|
$exitValue = WEXITSTATUS($?);
|
||||||
|
$signalNum = WIFSIGNALED($?) && WTERMSIG($?);
|
||||||
|
$dumpedCore = WIFSIGNALED($?) && ($? & 128);
|
||||||
|
}
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
warn "Error running $shellCommand: $@\n";
|
warn "Error running $shellCommand: $@\n";
|
||||||
$output = $@;
|
$output = $@;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Restore external state
|
||||||
chdir($cwd) if (defined($changeToDir));
|
chdir($cwd) if (defined($changeToDir));
|
||||||
|
if ($printOutputImmediately) {
|
||||||
|
my $prevFd = select(STDOUT);
|
||||||
|
$| = $prevStdoutBufferingSetting;
|
||||||
|
select($prevFd);
|
||||||
|
}
|
||||||
|
|
||||||
return { startTime => $childStartedTime,
|
return { startTime => $childStartedTime,
|
||||||
endTime => $childEndedTime,
|
endTime => $childEndedTime,
|
||||||
|
|
Загрузка…
Ссылка в новой задаче