mirror of
https://github.com/git/git.git
synced 2024-11-23 18:05:29 +08:00
62c71ace44
Since18d8c26930
(test_terminal: redirect child process' stdin to a pty, 2015-08-04), we set up a pty and copy stdin to the child program. But this ends up being racy; once we send all of the bytes and close the descriptor, the child program will no longer see a terminal! isatty() will return 0, and trying to read may return EIO, even if we didn't yet get all of the bytes. This was mentioned even in the commit message of18d8c26930
, but we hacked around it by just sending an infinite input from /dev/zero (in the intended case, we only cared about isatty(0), not reading actual input). And it came up again recently in: https://lore.kernel.org/git/d42a55b1-1ba9-4cfb-9c3d-98ea4d86da33@gmail.com/ where we tried to actually send bytes, but they don't always all come through. So this interface is somewhat of an accident waiting to happen; a caller might not even care about stdin being a tty, but will get bit by the flaky behavior. One solution would probably be to avoid closing test_terminal's end of the pty altogether. But then the other side would never see EOF on its stdin. That may be OK for some cases, but it's another gotcha that might cause races or deadlocks, depending on what the child expects to read. Let's instead just drop test_terminal's stdin feature completely. Since the previous commit dropped the two cases from t4153 for which the feature was originally added, there are no callers left that need it. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
83 lines
1.8 KiB
Perl
Executable File
83 lines
1.8 KiB
Perl
Executable File
#!/usr/bin/perl
|
|
use 5.008001;
|
|
use strict;
|
|
use warnings;
|
|
use IO::Pty;
|
|
use File::Copy;
|
|
|
|
# Run @$argv in the background with stdio redirected to $out and $err.
|
|
sub start_child {
|
|
my ($argv, $out, $err) = @_;
|
|
my $pid = fork;
|
|
if (not defined $pid) {
|
|
die "fork failed: $!"
|
|
} elsif ($pid == 0) {
|
|
open STDOUT, ">&", $out;
|
|
open STDERR, ">&", $err;
|
|
close $out;
|
|
exec(@$argv) or die "cannot exec '$argv->[0]': $!"
|
|
}
|
|
return $pid;
|
|
}
|
|
|
|
# Wait for $pid to finish.
|
|
sub finish_child {
|
|
# Simplified from wait_or_whine() in run-command.c.
|
|
my ($pid) = @_;
|
|
|
|
my $waiting = waitpid($pid, 0);
|
|
if ($waiting < 0) {
|
|
die "waitpid failed: $!";
|
|
} elsif ($? & 127) {
|
|
my $code = $? & 127;
|
|
warn "died of signal $code";
|
|
return $code + 128;
|
|
} else {
|
|
return $? >> 8;
|
|
}
|
|
}
|
|
|
|
sub xsendfile {
|
|
my ($out, $in) = @_;
|
|
|
|
# Note: the real sendfile() cannot read from a terminal.
|
|
|
|
# It is unspecified by POSIX whether reads
|
|
# from a disconnected terminal will return
|
|
# EIO (as in AIX 4.x, IRIX, and Linux) or
|
|
# end-of-file. Either is fine.
|
|
copy($in, $out, 4096) or $!{EIO} or die "cannot copy from child: $!";
|
|
}
|
|
|
|
sub copy_stdio {
|
|
my ($out, $err) = @_;
|
|
my $pid = fork;
|
|
defined $pid or die "fork failed: $!";
|
|
if (!$pid) {
|
|
close($out);
|
|
xsendfile(\*STDERR, $err);
|
|
exit 0;
|
|
}
|
|
close($err);
|
|
xsendfile(\*STDOUT, $out);
|
|
finish_child($pid) == 0
|
|
or exit 1;
|
|
}
|
|
|
|
if ($#ARGV < 1) {
|
|
die "usage: test-terminal program args";
|
|
}
|
|
$ENV{TERM} = 'vt100';
|
|
my $parent_out = new IO::Pty;
|
|
my $parent_err = new IO::Pty;
|
|
$parent_out->set_raw();
|
|
$parent_err->set_raw();
|
|
$parent_out->slave->set_raw();
|
|
$parent_err->slave->set_raw();
|
|
my $pid = start_child(\@ARGV, $parent_out->slave, $parent_err->slave);
|
|
close $parent_out->slave;
|
|
close $parent_err->slave;
|
|
copy_stdio($parent_out, $parent_err);
|
|
my $ret = finish_child($pid);
|
|
exit($ret);
|