From: Eric Wong Date: Thu, 3 Mar 2016 03:12:12 +0000 (+0000) Subject: git: support an error tmpfile for stashing stderr output X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=64d131a6b4435289f8876b20510a6d80d4dde418;p=thirdparty%2Fpublic-inbox.git git: support an error tmpfile for stashing stderr output This should allow us to avoid polluting stderr output when HTTP clients inevitably request broken revisions. --- diff --git a/lib/PublicInbox/Git.pm b/lib/PublicInbox/Git.pm index bd945007f..2b6782a75 100644 --- a/lib/PublicInbox/Git.pm +++ b/lib/PublicInbox/Git.pm @@ -12,6 +12,8 @@ use warnings; use POSIX qw(dup2); require IO::Handle; use PublicInbox::Spawn qw(spawn popen_rd); +use IO::File; +use Fcntl qw(:seek); # Documentation/SubmittingPatches recommends 12 (Linux v4.4) my $abbrev = `git config core.abbrev` || 12; @@ -20,7 +22,25 @@ sub abbrev { "--abbrev=$abbrev" } sub new { my ($class, $git_dir) = @_; - bless { git_dir => $git_dir }, $class + bless { git_dir => $git_dir, err => IO::File->new_tmpfile }, $class +} + +sub err_begin ($) { + my $err = $_[0]->{err}; + sysseek($err, 0, SEEK_SET) or die "sysseek failed: $!"; + truncate($err, 0) or die "truncate failed: $!"; + my $ret = fileno($err); + defined $ret or die "fileno failed: $!"; + $ret; +} + +sub err ($) { + my $err = $_[0]->{err}; + sysseek($err, 0, SEEK_SET) or die "sysseek failed: $!"; + defined(sysread($err, my $buf, -s $err)) or die "sysread failed: $!"; + sysseek($err, 0, SEEK_SET) or die "sysseek failed: $!"; + truncate($err, 0) or die "truncate failed: $!"; + $buf; } sub _bidi_pipe { diff --git a/t/git.t b/t/git.t index e09a4d011..e7a3c9ea9 100644 --- a/t/git.t +++ b/t/git.t @@ -141,27 +141,25 @@ if (1) { { my $git = PublicInbox::Git->new($dir); - open my $tmperr, '>&', \*STDERR or die "dup stderr failed: $!\n"; - open STDERR, '>', '/dev/null' or die "redirect stderr failed: $!\n"; - my $err = $git->popen(qw(cat-file blob non-existent)); + my $err = $git->popen([qw(cat-file blob non-existent)], undef, + { 2 => $git->err_begin }); my @out = <$err>; my $close_ret = close $err; my $close_err = $?; - open STDERR, '>&', $tmperr or die "restore stderr failed: $!\n"; is(join('', @out), '', 'no output on stdout on error'); isnt($close_err, 0, 'close set $? on bad command'); ok(!$close_ret, 'close returned error on bad command'); + isnt($git->err, '', 'got stderr output'); - open STDERR, '>', '/dev/null' or die "redirect stderr failed: $!\n"; - $err = $git->popen(qw(tag -l)); + $err = $git->popen([qw(tag -l)], undef, { 2 => $git->err_begin }); @out = <$err>; $close_ret = close $err; $close_err = $?; - open STDERR, '>&', $tmperr or die "restore stderr failed: $!\n"; is(join('', @out), '', 'no output on stdout on error'); ok(!$close_err, 'close clobbered $? on empty output'); ok($close_ret, 'close returned error on empty output'); + is($git->err, '', 'no stderr output'); } done_testing();