diff --git a/btrbk b/btrbk index b46eb6b..9f9798d 100755 --- a/btrbk +++ b/btrbk @@ -285,7 +285,6 @@ my $quiet; my @exclude_vf; my $do_dumper; my $show_progress = 0; -my $err = ""; my $output_format; my $lockfile; my $tlog_fh; @@ -294,6 +293,7 @@ my $current_transaction; my @transaction_log; my %config_override; my @tm_now; # current localtime ( sec, min, hour, mday, mon, year, wday, yday, isdst ) +my @stderr; # stderr of last run_cmd my %warn_once; my %kdf_vars; my $kdf_session_key; @@ -687,16 +687,15 @@ sub run_cmd(@) my @cmd_pipe_in = (ref($_[0]) eq "HASH") ? @_ : { @_ }; die unless(scalar(@cmd_pipe_in)); - $err = ""; + @stderr = (); my $destructive = 0; - my $catch_stderr = 0; - my $exitcode_loglevel = "debug"; - my $filter_stderr = undef; my @cmd_pipe; my @unsafe_cmd; my $compressed = undef; my $stream_options = $cmd_pipe_in[0]->{stream_options} // {}; + my @filter_stderr; + my $fatal_stderr; my $has_rsh; $cmd_pipe_in[0]->{stream_source} = 1; @@ -706,10 +705,9 @@ sub run_cmd(@) { die if(defined($href->{cmd_text})); - $catch_stderr = 1 if($href->{catch_stderr}); - $filter_stderr = $href->{filter_stderr} if($href->{filter_stderr}); # NOTE: last filter wins! + push @filter_stderr, ((ref($href->{filter_stderr}) eq "ARRAY") ? @{$href->{filter_stderr}} : $href->{filter_stderr}) if($href->{filter_stderr}); + $fatal_stderr = $href->{fatal_stderr} if($href->{fatal_stderr}); $destructive = 1 unless($href->{non_destructive}); - $exitcode_loglevel = $href->{exitcode_loglevel} if($href->{exitcode_loglevel}); $has_rsh = 1 if($href->{rsh}); if($href->{check_unsafe}) { @@ -819,15 +817,15 @@ sub run_cmd(@) # execute command - my ($pid, $out_fh, $err_fh, @stdout, @stderr); + my ($pid, $out_fh, $err_fh, @stdout); $err_fh = gensym; if(eval_quiet { $pid = open3(undef, $out_fh, $err_fh, $cmd); }) { chomp(@stdout = readline($out_fh)); chomp(@stderr = readline($err_fh)); waitpid($pid, 0); if($loglevel >= 4) { - TRACE "[stdout] $_" foreach(@stdout); - TRACE "[stderr] $_" foreach(@stderr); + TRACE map("[stdout] $_", @stdout); + TRACE map("[stderr] $_", @stderr); } } else { @@ -847,26 +845,22 @@ sub run_cmd(@) } my $exitcode = $? >> 8; - my @filtered_err; - if($filter_stderr) { - @filtered_err = map { &{$filter_stderr} ($exitcode) // () } @stderr; + # call hooks: fatal_stderr, filter_stderr + if(($exitcode == 0) && $fatal_stderr) { + $exitcode = -1 if(grep &{$fatal_stderr}(), @stderr); } - elsif($has_rsh && ($exitcode == 255)) { - # SSH returns exit status 255 if an error occurred. - @filtered_err = map { { error => $_ } } @stderr; - $exitcode_loglevel = "error"; + foreach my $filter_fn (@filter_stderr) { + @stderr = map { &{$filter_fn} ($exitcode); $_ // () } @stderr; } - if($exitcode || (grep { $_->{fatal} } @filtered_err)) { - my $log_text = "Command execution failed (exitcode=$exitcode): `$cmd`"; - $exitcode_loglevel = "error" if(scalar(@filtered_err)); - if($exitcode_loglevel eq "error") { ERROR $log_text; } - elsif($exitcode_loglevel eq "warn") { WARN $log_text; } - else { DEBUG $log_text; } - foreach(@filtered_err) { - ERROR "... $_->{error}" if(defined($_->{error})); - WARN "... $_->{warn}" if(defined($_->{warn})); - DEBUG "... $_->{debug}" if(defined($_->{debug})); + unshift @stderr, "sh: $cmd"; + + if($exitcode) { + my @log_text = ("Command execution failed (exitcode=$exitcode): `$cmd`", map("... $_", @stderr)); + if($has_rsh && ($exitcode == 255)) { # SSH returns exit status 255 if an error occurred. + ERROR @log_text; + } else { + DEBUG @log_text; } return undef; } @@ -877,13 +871,25 @@ sub run_cmd(@) } +sub _btrfs_filter_stderr +{ + if(/^usage: / || /(unrecognized|invalid) option/) { + WARN_ONCE "Using unsupported btrfs-progs < v$BTRFS_PROGS_MIN"; + } + # strip error prefix (we print our own) + # note that this also affects ssh_filter_btrbk.sh error strings + s/^ERROR: //; +} + + sub btrfs_filesystem_show($) { my $vol = shift || die; my $path = $vol->{PATH} // die; return run_cmd( cmd => vinfo_cmd($vol, "btrfs filesystem show", { unsafe => $path } ), rsh => vinfo_rsh($vol), - non_destructive => 1 + non_destructive => 1, + filter_stderr => \&_btrfs_filter_stderr, ); } @@ -894,7 +900,8 @@ sub btrfs_filesystem_df($) my $path = $vol->{PATH} // die; return run_cmd( cmd => vinfo_cmd($vol, "btrfs filesystem df", { unsafe => $path }), rsh => vinfo_rsh($vol), - non_destructive => 1 + non_destructive => 1, + filter_stderr => \&_btrfs_filter_stderr, ); } @@ -905,8 +912,14 @@ sub btrfs_filesystem_usage($) my $path = $vol->{PATH} // die; my $ret = run_cmd( cmd => vinfo_cmd($vol, "btrfs filesystem usage", { unsafe => $path } ), rsh => vinfo_rsh($vol), - non_destructive => 1 + non_destructive => 1, + filter_stderr => \&_btrfs_filter_stderr, ); + unless(defined($ret)) { + ERROR "Failed to fetch btrfs filesystem usage for: $vol->{PRINT}", map("... $_", @stderr); + return undef; + } + return undef unless(defined($ret)); my %detail; @@ -978,21 +991,7 @@ sub btrfs_subvolume_show($;@) my $ret = run_cmd(cmd => vinfo_cmd($vol, "btrfs subvolume show", @cmd_options, { unsafe => $path }), rsh => vinfo_rsh($vol), non_destructive => 1, - catch_stderr => 1, # hack for shell-based run_cmd() - filter_stderr => sub { - return undef unless($_[0]); # do nothing if exitcode=0 - if(s/^ERROR: //) { # catch errors from btrfs-progs as well as ssh_filter_btrbk.sh - $err = $_; - return { error => $_, fatal => 1 } if(/^ssh_filter_btrbk.sh/); # "fatal" causes run_cmd to return undef - } elsif(/(unrecognized|invalid) option/) { - WARN_ONCE "$_ (maybe using unsupported btrfs-progs < v$BTRFS_PROGS_MIN " . ($vol->{HOST} ? "on host=$vol->{HOST} " : "") . "?)"; - } else { - DEBUG "Unparsed error: $_"; - $err ||= $_; - } - # soft fail: $err can be displayed as a user-friendly WARNING - return undef; - }, + filter_stderr => \&_btrfs_filter_stderr, ); return undef unless(defined($ret)); @@ -1103,6 +1102,7 @@ sub btrfs_subvolume_list_readonly_flag($) my $ret = run_cmd(cmd => vinfo_cmd($vol, "btrfs subvolume list", '-a', '-r', { unsafe => $path } ), rsh => vinfo_rsh($vol), non_destructive => 1, + filter_stderr => \&_btrfs_filter_stderr, ); return undef unless(defined($ret)); @@ -1135,6 +1135,7 @@ sub btrfs_subvolume_list($;@) my $ret = run_cmd(cmd => vinfo_cmd($vol, "btrfs subvolume list", @filter_options, @display_options, { unsafe => $path } ), rsh => vinfo_rsh($vol), non_destructive => 1, + filter_stderr => \&_btrfs_filter_stderr, ); return undef unless(defined($ret)); @@ -1219,9 +1220,10 @@ sub btrfs_subvolume_find_new($$;$) my $ret = run_cmd(cmd => vinfo_cmd($vol, "btrfs subvolume find-new", { unsafe => $path }, $lastgen ), rsh => vinfo_rsh($vol), non_destructive => 1, + filter_stderr => \&_btrfs_filter_stderr, ); unless(defined($ret)) { - ERROR "Failed to fetch modified files for: $vol->{PRINT}"; + ERROR "Failed to fetch modified files for: $vol->{PRINT}", map("... $_", @stderr); return undef; } @@ -1285,10 +1287,11 @@ sub btrfs_subvolume_snapshot($$) ); my $ret = run_cmd(cmd => vinfo_cmd($svol, "btrfs subvolume snapshot", '-r', { unsafe => $src_path }, { unsafe => $target_path } ), rsh => vinfo_rsh($svol), + filter_stderr => \&_btrfs_filter_stderr, ); end_transaction("snapshot", defined($ret)); unless(defined($ret)) { - ERROR "Failed to create btrfs subvolume snapshot: $svol->{PRINT} -> $target_path"; + ERROR "Failed to create snapshot: $svol->{PRINT} -> $target_path", map("... $_", @stderr); return undef; } return $target_vol; @@ -1338,45 +1341,35 @@ sub btrfs_subvolume_delete($@) } $ret = run_cmd(cmd => ['rm', '-f', @cmd_target_paths ], rsh => vinfo_rsh($targets->[0]), - catch_stderr => 1, # hack for shell-based run_cmd() - filter_stderr => sub { - # catch errors from "rm -f" - if(/^rm: cannot remove '($file_match)':/) { - my $catch = $1; # make sure $catch matches $vol->{PATH} - $catch =~ s/\.info$//; - $catch =~ s/\.split_[a-z][a-z]$//; - $err_catch{$catch} //= []; - push(@{$err_catch{$catch}}, $_); - return undef; # no error messages in run_cmd - } - else { - # show errors in run_cmd; force "Command execution failed" error message - return { error => $_, fatal => 1 }; - } - }, ); + unless(defined($ret)) { + foreach(@stderr) { + next unless(/^rm: cannot remove '($file_match)':/); + my $catch = $1; # make sure $catch matches $vol->{PATH} + $catch =~ s/\.info$//; + $catch =~ s/\.split_[a-z][a-z]$//; + $err_catch{$catch} //= []; + push(@{$err_catch{$catch}}, $_); + } + } } else { my @cmd_target_paths = map { { unsafe => $_->{PATH} } } @$targets; - my $unparsed_errors; my @options; @options = ("--commit-$commit") if($commit); $ret = run_cmd(cmd => vinfo_cmd($targets->[0], "btrfs subvolume delete", @options, @cmd_target_paths ), rsh => vinfo_rsh($targets->[0]), - catch_stderr => 1, # hack for shell-based run_cmd() - filter_stderr => sub { - return undef unless(s/^ERROR: //); # strip ERROR prefix - if(/'($file_match)'/ || /: ($file_match)$/ || /($file_match):/) { - # NOTE: as of btrfs-progs-4.16, this does not catch anything - $err_catch{$1} //= []; - push(@{$err_catch{$1}}, $_); - } else { - $unparsed_errors = 1; - } - return undef; # no error messages in run_cmd - }, + fatal_stderr => sub { m/^ERROR: /; }, # probably not needed, "btrfs sub delete" returns correct exit status + filter_stderr => \&_btrfs_filter_stderr, ); - $ret = undef if($unparsed_errors); + unless(defined($ret)) { + foreach(@stderr) { + next unless(/'($file_match)'/ || /: ($file_match)$/ || /($file_match):/); + # NOTE: as of btrfs-progs-4.16, this does not catch anything + $err_catch{$1} //= []; + push(@{$err_catch{$1}}, $_); + } + } } if(defined($ret)) { @@ -1388,7 +1381,7 @@ sub btrfs_subvolume_delete($@) foreach my $check_target (@$targets) { my $err_ary = $err_catch{$check_target->{PATH}}; if($err_ary) { - ERROR "Failed to delete subvolume \"$check_target->{PRINT}\": $_" foreach(@$err_ary); + ERROR map("Failed to delete subvolume \"$check_target->{PRINT}\": $_", @$err_ary); $catch_count++; } else { @@ -1398,9 +1391,9 @@ sub btrfs_subvolume_delete($@) @deleted = () if($catch_count != (scalar keys %err_catch)); } unless(scalar(@deleted)) { - ERROR "Failed to match error messages from delete command, assuming nothing deleted:"; - ERROR "... possibly not deleted subvolume: $_" foreach(map( { $_->{PRINT} } @$targets)); - ERROR "... consider running 'btrbk prune -n'"; + ERROR "Failed to match error messages from delete command, assuming nothing deleted", map("... $_", @stderr); + ERROR map("Possibly not deleted subvolume: $_->{PRINT}", @$targets); + ERROR "Consider running 'btrbk prune -n'"; } } @@ -1426,10 +1419,11 @@ sub btrfs_qgroup_destroy($@) vinfo_prefixed_keys("target", $vol)); my $ret = run_cmd(cmd => vinfo_cmd($vol, "btrfs qgroup destroy", $qgroup_id, { unsafe => $path }), rsh => vinfo_rsh($vol), + filter_stderr => \&_btrfs_filter_stderr, ); end_transaction($opts{type} // "qgroup_destroy", defined($ret)); unless(defined($ret)) { - ERROR "Failed to destroy qgroup \"$qgroup_id\" for subvolume: $vol->{PRINT}"; + ERROR "Failed to destroy qgroup \"$qgroup_id\" for subvolume: $vol->{PRINT}", map("... $_", @stderr); return undef; } return $vol; @@ -1472,21 +1466,14 @@ sub btrfs_send_receive($$;$$$) rsh => vinfo_rsh($snapshot, disable_compression => $stream_options->{stream_compress}), name => "btrfs send", stream_options => $stream_options, - exitcode_loglevel => "error", # print error message if exitcode != 0 - catch_stderr => 1, # hack for shell-based run_cmd() + filter_stderr => [ \&_btrfs_filter_stderr, sub { $_ = undef if(/^At subvol/) } ], }; push @cmd_pipe, { cmd => vinfo_cmd($target, "btrfs receive", @receive_options, { unsafe => $target_path . '/' } ), rsh => vinfo_rsh($target, disable_compression => $stream_options->{stream_compress}), name => "btrfs receive", - catch_stderr => 1, # hack for shell-based run_cmd() - filter_stderr => sub { - # NOTE: btrfs-progs < 4.11: if "btrfs send" fails, "btrfs receive" returns 0! - return { error => $_, fatal => 1 } if(s/^ERROR: //); # "fatal" causes run_cmd to return undef - return { warn => $_ } if(s/^WARNING: //); - return undef; - }, + fatal_stderr => sub { m/^ERROR: /; }, # NOTE: btrfs-progs < 4.11: if "btrfs send" fails, "btrfs receive" returns 0! }; my $send_receive_error = 0; @@ -1496,7 +1483,9 @@ sub btrfs_send_receive($$;$$$) vinfo_prefixed_keys("parent", $parent), ); my $ret = run_cmd(@cmd_pipe); + my @cmd_err; unless(defined($ret)) { + @cmd_err = @stderr; # save for later $send_receive_error = 1; } @@ -1522,21 +1511,21 @@ sub btrfs_send_receive($$;$$$) unless($send_receive_error) { # plausibility checks on target detail unless($detail->{readonly}) { - ERROR "[send/receive] target is not readonly: $vol_received->{PRINT}"; + push @cmd_err, "target is not readonly: $vol_received->{PRINT}"; $send_receive_error = 1; } if($detail->{received_uuid} && ($detail->{received_uuid} eq '-')) { # NOTE: received_uuid is not in @required_keys (needs btrfs-progs >= 4.1 (BTRFS_PROGS_MIN)) # so we only check it if it's really present - ERROR "[send/receive] received_uuid is not set on target: $vol_received->{PRINT}"; + push @cmd_err, "received_uuid is not set on target: $vol_received->{PRINT}"; $send_receive_error = 1; } if($parent && ($detail->{parent_uuid} eq '-')) { - ERROR "[send/receive] parent_uuid is not set on target: $vol_received->{PRINT}"; + push @cmd_err, "parent_uuid is not set on target: $vol_received->{PRINT}"; $send_receive_error = 1; } if((not $parent) && ($detail->{parent_uuid} ne '-')) { - ERROR "[send/receive] parent_uuid is set on target: $vol_received->{PRINT}"; + push @cmd_err, "parent_uuid is set on target: $vol_received->{PRINT}"; $send_receive_error = 1; } } @@ -1545,7 +1534,7 @@ sub btrfs_send_receive($$;$$$) $is_garbled = ((not $detail->{readonly}) && defined($detail->{received_uuid}) && ($detail->{received_uuid} eq '-')); } else { - $err = "" if($send_receive_error); # ignore $err if send/receive failed + push @cmd_err, "failed to check target subvolume: $vol_received->{PRINT}", @stderr; $send_receive_error = 1; } } @@ -1554,7 +1543,7 @@ sub btrfs_send_receive($$;$$$) if($send_receive_error) { ERROR "Failed to send/receive subvolume: $snapshot->{PRINT} " . ($parent_path ? "[$parent_path]" : "") . " -> $vol_received->{PRINT}"; - ERROR "... $err" if($err); + ERROR map("... $_", @cmd_err); } if($is_garbled) { @@ -1619,13 +1608,8 @@ sub btrfs_send_to_file($$$;$$) rsh => vinfo_rsh($source, disable_compression => $stream_options->{stream_compress}), name => "btrfs send", stream_options => $stream_options, - exitcode_loglevel => "error", # print error message if exitcode != 0 - catch_stderr => 1, # hack for shell-based run_cmd() - filter_stderr => sub { - return { error => $_, fatal => 1 } if(s/^ERROR: //); # "fatal" causes run_cmd to return undef - return { warn => $_ } if(s/^WARNING: //); - return undef; - }, + filter_stderr => [ \&_btrfs_filter_stderr, sub { $_ = undef if(/^At subvol/) } ], + fatal_stderr => sub { m/^ERROR: /; }, }; if($compress) { @@ -1702,12 +1686,18 @@ sub btrfs_send_to_file($$$;$$) DEBUG "Generating session key for: $vol_received->{PRINT}"; my $kdf_backend_name = $encrypt->{kdf_backend}; $kdf_backend_name =~ s/^.*\///; + my $key_target_text = $encrypt->{kdf_keygen_each} ? "\"$vol_received->{PRINT}\"" : "all raw backups"; - print STDOUT "\nGenerate session key for " . ($encrypt->{kdf_keygen_each} ? "\"$vol_received->{PRINT}\"" : "all raw backups") . ":\n"; + print STDOUT "\nGenerate session key for $key_target_text:\n"; my $kdf_values = run_cmd(cmd => [ $encrypt->{kdf_backend}, $encrypt->{kdf_keysize} ], non_destructive => 1, name => $kdf_backend_name ); + unless(defined($kdf_values)) { + ERROR "Failed to generate session key for $key_target_text", map("... $_", @stderr); + return undef; + } + return undef unless(defined($kdf_values)); foreach(@$kdf_values) { chomp; @@ -1803,8 +1793,13 @@ sub btrfs_send_to_file($$$;$$) my $ret; $ret = system_write_raw_info($vol_received, \%raw_info); + my @cmd_err; if(defined($ret)) { $ret = run_cmd(@cmd_pipe); + @cmd_err = @stderr unless(defined($ret)); # save for later + } + else { + push @cmd_err, "failed to write raw .info file: $vol_received->{PATH}.info", @stderr; } if(defined($ret)) { @@ -1813,9 +1808,10 @@ sub btrfs_send_to_file($$$;$$) # redirection as well as "dd" always creates the target file. # Note that "split" does not create empty files. my $test_postfix = ($split ? ".split_aa" : ""); - DEBUG "Testing target data file (non-zero size)"; + my $check_file = "${target_path}/${target_filename}${test_postfix}"; + DEBUG "Testing target data file (non-zero size): $check_file"; $ret = run_cmd({ - cmd => ['test', '-s', { unsafe => "${target_path}/${target_filename}${test_postfix}" } ], + cmd => ['test', '-s', { unsafe => $check_file } ], rsh => vinfo_rsh($target), name => "test", }); @@ -1824,10 +1820,14 @@ sub btrfs_send_to_file($$$;$$) delete $raw_info{INCOMPLETE}; $ret = system_write_raw_info($vol_received, \%raw_info); } + else { + push @cmd_err, "failed to check target file (not present or zero length): $check_file"; + } } end_transaction("send-to-raw", defined($ret)); unless(defined($ret)) { ERROR "Failed to send btrfs subvolume to raw file: $source->{PRINT} " . ($parent_path ? "[$parent_path]" : "") . " -> $vol_received->{PRINT}"; + ERROR map("... $_", @cmd_err); return undef; } return 1; @@ -1841,8 +1841,6 @@ sub system_list_mountinfo($) my $ret = run_cmd(cmd => [ qw(cat), $file ], rsh => vinfo_rsh($vol), non_destructive => 1, - exitcode_loglevel => "error", # print error message if exitcode != 0 - catch_stderr => 1, # hack for shell-based run_cmd() ); return undef unless(defined($ret)); @@ -5195,11 +5193,11 @@ MAIN: # NOTE: ssh://{src,target} uses default config my $src_vol = vinfo($src_url, $config); - unless(vinfo_init_root($src_vol)) { ERROR "Failed to fetch subvolume detail for '$src_vol->{PRINT}'" . ($err ? ": $err" : ""); exit 1; } + unless(vinfo_init_root($src_vol)) { ERROR "Failed to fetch subvolume detail for '$src_vol->{PRINT}'", map("... $_", @stderr); exit 1; } if($src_vol->{node}{is_root}) { ERROR "Subvolume is btrfs root: $src_vol->{PRINT}"; exit 1; } my $target_vol = vinfo($target_url, $config); - unless(vinfo_init_root($target_vol)) { ERROR "Failed to fetch subvolume detail for '$target_vol->{PRINT}'" . ($err ? ": $err" : ""); exit 1; } + unless(vinfo_init_root($target_vol)) { ERROR "Failed to fetch subvolume detail for '$target_vol->{PRINT}'", map("... $_", @stderr); exit 1; } if($target_vol->{node}{is_root}) { ERROR "Subvolume is btrfs root: $target_vol->{PRINT}"; exit 1; } unless(_is_same_fs_tree($src_vol->{node}, $target_vol->{node})) { @@ -5301,7 +5299,7 @@ MAIN: $realpath_cache{$mnt->{mount_point}} = $mnt->{mount_point}; # we know those are real paths, prevents calling readlink in btrfs_mountpoint my $vol = vinfo($mnt->{mount_point}, $config); unless(vinfo_init_root($vol)) { - ERROR "Failed to fetch subvolume detail for: $vol->{PRINT}" . ($err ? ": $err" : ""); + ERROR "Failed to fetch subvolume detail for: $vol->{PRINT}", map("... $_", @stderr); exit 1; } @@ -5381,12 +5379,12 @@ MAIN: my $src_root = vinfo($src_url, $config); unless(vinfo_init_root($src_root)) { - ERROR "Failed to fetch subvolume detail for '$src_root->{PRINT}'" . ($err ? ": $err" : ""); + ERROR "Failed to fetch subvolume detail for '$src_root->{PRINT}'", map("... $_", @stderr); exit 1; } my $archive_root = vinfo($archive_url, $config); unless($archive_raw ? vinfo_init_raw_root($archive_root) : vinfo_init_root($archive_root)) { - ERROR "Failed to fetch " . ($archive_raw ? "raw target metadata" : "subvolume detail") . " for '$archive_root->{PRINT}'" . ($err ? ": $err" : ""); + ERROR "Failed to fetch " . ($archive_raw ? "raw target metadata" : "subvolume detail") . " for '$archive_root->{PRINT}'", map("... $_", @stderr); exit 1; } @@ -5421,18 +5419,18 @@ MAIN: my $sroot = vinfo($sroot_url, $config_sroot); vinfo_assign_config($sroot); unless(vinfo_init_root($sroot)) { - ABORTED($sroot, "Failed to fetch subvolume detail" . ($err ? ": $err" : "")); - WARN "Skipping archive source \"$sroot->{PRINT}\": " . ABORTED_TEXT($sroot); + ABORTED($sroot, "Failed to fetch subvolume detail"); + WARN "Skipping archive source \"$sroot->{PRINT}\": " . ABORTED_TEXT($sroot), map("... $_", @stderr); next; } my $droot = vinfo($droot_url, $config_droot); vinfo_assign_config($droot); unless($archive_raw ? vinfo_init_raw_root($droot) : vinfo_init_root($droot)) { - DEBUG "Failed to fetch " . ($archive_raw ? "raw target metadata" : "subvolume detail") . " for '$droot->{PRINT}'" . ($err ? ": $err" : ""); + DEBUG "Failed to fetch " . ($archive_raw ? "raw target metadata" : "subvolume detail") . " for '$droot->{PRINT}'"; unless(system_mkdir($droot)) { ABORTED($droot, "Failed to create directory: $droot->{PRINT}/"); - WARN "Skipping archive target \"$droot->{PRINT}\": " . ABORTED_TEXT($droot); + WARN "Skipping archive target \"$droot->{PRINT}\": " . ABORTED_TEXT($droot), map("... $_", @stderr); next; } $droot->{SUBDIR_CREATED} = 1; @@ -5445,8 +5443,8 @@ MAIN: else { # after directory is created, try to init again unless($archive_raw ? vinfo_init_raw_root($droot) : vinfo_init_root($droot)) { - ABORTED($droot, "Failed to fetch subvolume detail" . ($err ? ": $err" : "")); - WARN "Skipping archive target \"$droot->{PRINT}\": " . ABORTED_TEXT($droot); + ABORTED($droot, "Failed to fetch subvolume detail"); + WARN "Skipping archive target \"$droot->{PRINT}\": " . ABORTED_TEXT($droot), map("... $_", @stderr); next; } } @@ -5616,8 +5614,8 @@ MAIN: next unless(grep defined($_->{GLOB_CONTEXT}), @{$config_vol->{SUBSECTION}}); my $sroot = vinfo($config_vol->{url}, $config_vol); unless(vinfo_init_root($sroot)) { - ABORTED($sroot, "Failed to fetch subvolume detail" . ($err ? ": $err" : "")); - WARN "Skipping volume \"$sroot->{PRINT}\": " . ABORTED_TEXT($sroot); + ABORTED($sroot, "Failed to fetch subvolume detail"); + WARN "Skipping volume \"$sroot->{PRINT}\": " . ABORTED_TEXT($sroot), map("... $_", @stderr); next; } @@ -5953,15 +5951,15 @@ MAIN: foreach my $sroot (vinfo_subsection($config, 'volume')) { DEBUG "Initializing volume section: $sroot->{PRINT}"; unless(vinfo_init_root($sroot)) { - ABORTED($sroot, "Failed to fetch subvolume detail" . ($err ? ": $err" : "")); - WARN "Skipping volume \"$sroot->{PRINT}\": " . ABORTED_TEXT($sroot); + ABORTED($sroot, "Failed to fetch subvolume detail"); + WARN "Skipping volume \"$sroot->{PRINT}\": " . ABORTED_TEXT($sroot), map("... $_", @stderr); next; } foreach my $svol (vinfo_subsection($sroot, 'subvolume')) { DEBUG "Initializing subvolume section: $svol->{PRINT}"; unless(vinfo_init_root($svol)) { - ABORTED($svol, "Failed to fetch subvolume detail" . ($err ? ": $err" : "")); - WARN "Skipping subvolume \"$svol->{PRINT}\": " . ABORTED_TEXT($svol); + ABORTED($svol, "Failed to fetch subvolume detail"); + WARN "Skipping subvolume \"$svol->{PRINT}\": " . ABORTED_TEXT($svol), map("... $_", @stderr); next; } if((not $svol->{node}{uuid}) || ($svol->{node}{uuid} eq '-')) { @@ -5991,8 +5989,8 @@ MAIN: my $snaproot = vinfo_snapshot_root($svol); unless(vinfo_init_root($snaproot)) { - ABORTED($svol, "Failed to fetch subvolume detail for snapshot_dir" . ($err ? ": $err" : "")); - WARN "Skipping subvolume \"$svol->{PRINT}\": " . ABORTED_TEXT($svol); + ABORTED($svol, "Failed to fetch subvolume detail for snapshot_dir"); + WARN "Skipping subvolume \"$svol->{PRINT}\": " . ABORTED_TEXT($svol), map("... $_", @stderr); next; } unless(_is_same_fs_tree($snaproot->{node}, $svol->{node})) { @@ -6018,16 +6016,16 @@ MAIN: if($target_type eq "send-receive") { unless(vinfo_init_root($droot)) { - ABORTED($droot, "Failed to fetch subvolume detail" . ($err ? ": $err" : "")); - WARN "Skipping target \"$droot->{PRINT}\": " . ABORTED_TEXT($droot); + ABORTED($droot, "Failed to fetch subvolume detail"); + WARN "Skipping target \"$droot->{PRINT}\": " . ABORTED_TEXT($droot), map("... $_", @stderr); next; } } elsif($target_type eq "raw") { unless(vinfo_init_raw_root($droot)) { - ABORTED($droot, "Failed to fetch raw target metadata" . ($err ? ": $err" : "")); - WARN "Skipping target \"$droot->{PRINT}\": " . ABORTED_TEXT($droot); + ABORTED($droot, "Failed to fetch raw target metadata"); + WARN "Skipping target \"$droot->{PRINT}\": " . ABORTED_TEXT($droot), map("... $_", @stderr); next; } } @@ -6077,7 +6075,7 @@ MAIN: my $url = $subvol_args[0] || die; my $vol = vinfo($url, $config); unless(vinfo_init_root($vol)) { - ERROR "Failed to fetch subvolume detail for: $url" . ($err ? ": $err" : ""); + ERROR "Failed to fetch subvolume detail for: $url", map("... $_", @stderr); exit 1; } if($vol->{node}{is_root}) {