btrbk: bugfix: run_cmd: do not redirect all stderr output, as this kills progress-viewer (pv) output

pull/65/merge
Axel Burri 2016-01-19 17:52:27 +01:00
parent a1698ef4b0
commit 614438183f
2 changed files with 35 additions and 20 deletions

View File

@ -3,6 +3,7 @@ btrbk-current
* Bugfix: fix monthly schedule if older than 10 weeks (close: #59). * Bugfix: fix monthly schedule if older than 10 weeks (close: #59).
* Bugfix: fix sprintf used by config option "timestamp_format long" * Bugfix: fix sprintf used by config option "timestamp_format long"
when using perl-5.22.0 (close: #57). when using perl-5.22.0 (close: #57).
* Bugfix: fix "--progress" option (close: #64).
* Added "clean" command (close: #61). * Added "clean" command (close: #61).
* Added "-n, --dry-run" option. * Added "-n, --dry-run" option.
* Added configuration options "raw_target_compress_level", * Added configuration options "raw_target_compress_level",

54
btrbk
View File

@ -325,44 +325,57 @@ sub end_transaction($$)
sub run_cmd(@) sub run_cmd(@)
{ {
# shell-based implementation.
# this needs some redirection magic for filter_stderr to work.
# NOTE: multiple filters are not supported!
my @commands = (ref($_[0]) eq "HASH") ? @_ : { @_ }; my @commands = (ref($_[0]) eq "HASH") ? @_ : { @_ };
die unless(scalar(@commands));
$err = ""; $err = "";
my $cmd = "";
my $name = "";
my $destructive = 0; my $destructive = 0;
my $pipe = "";
my $catch_stderr = 0; my $catch_stderr = 0;
my $filter_stderr = undef; my $filter_stderr = undef;
foreach (@commands) { foreach (@commands) {
$_->{rsh} //= []; $_->{rsh} //= [];
$_->{cmd} = [ @{$_->{rsh}}, @{$_->{cmd}} ]; $_->{cmd} = [ @{$_->{rsh}}, @{$_->{cmd}} ];
$_->{cmd_text} = join(' ', map { s/\n/\\n/g; "'$_'" } @{$_->{cmd}}); # ugly escape of \n, do we need to escape others? $_->{cmd_text} = join(' ', map { s/\n/\\n/g; "'$_'" } @{$_->{cmd}}); # ugly escape of \n, do we need to escape others?
$name = $_->{name} // $_->{cmd_text}; $catch_stderr = 1 if($_->{catch_stderr});
$_->{_buf} = ''; $filter_stderr = $_->{filter_stderr} if($_->{filter_stderr}); # NOTE: last filter wins!
$cmd .= $pipe . $_->{cmd_text};
$pipe = ' | ';
if($_->{catch_stderr}) {
$catch_stderr = 1;
$filter_stderr = $_->{filter_stderr};
}
$destructive = 1 unless($_->{non_destructive}); $destructive = 1 unless($_->{non_destructive});
} }
my $cmd_print = join(' | ', map { $_->{cmd_text} } @commands);
if($dryrun && $destructive) { my $cmd = $cmd_print;
DEBUG "### (dryrun) $cmd";
return "";
}
DEBUG "### $cmd";
if($catch_stderr) { if($catch_stderr) {
if(scalar(@commands) == 1) { if(scalar(@commands) == 1) {
# no pipes, simply redirect stderr to stdout
$cmd .= ' 2>&1'; $cmd .= ' 2>&1';
} else { }
$cmd = '{ ' . $cmd . ' ; } 2>&1'; else
{
# pipe chain is more complicated, result is something like this:
# { btrfs send <src> 2>&3 | pv | btrfs receive <dst> 2>&3 ; } 3>&1
$cmd = "{ ";
my $pipe = "";
foreach (@commands) {
$cmd .= $pipe . $_->{cmd_text};
$cmd .= ' 2>&3' if($_->{catch_stderr});
$pipe = ' | ';
}
$cmd .= ' ; } 3>&1';
} }
} }
# hide redirection magic from debug output
if($dryrun && $destructive) {
DEBUG "### (dryrun) $cmd_print";
return "";
}
DEBUG "### $cmd_print";
# execute command and parse output
TRACE "Executing command: $cmd";
my $ret = ""; my $ret = "";
$ret = `$cmd`; $ret = `$cmd`;
chomp($ret); chomp($ret);
@ -375,7 +388,7 @@ sub run_cmd(@)
if($catch_stderr) { if($catch_stderr) {
$_ = $ret; $_ = $ret;
&{$filter_stderr} ($cmd) if($filter_stderr); &{$filter_stderr} ($cmd) if($filter_stderr);
ERROR "[$cmd] $_" if($_); ERROR "[$cmd_print] $_" if($_);
} }
return undef; return undef;
} }
@ -1277,6 +1290,7 @@ sub btrfs_send_receive($$$$)
cmd => [ qw(btrfs send), @send_options, $snapshot_path ], cmd => [ qw(btrfs send), @send_options, $snapshot_path ],
rsh => $snapshot->{RSH}, rsh => $snapshot->{RSH},
name => "btrfs send", name => "btrfs send",
catch_stderr => 1, # hack for shell-based run_cmd()
}; };
push @cmd_pipe, { push @cmd_pipe, {
cmd => [ '/usr/bin/pv' ], cmd => [ '/usr/bin/pv' ],