mirror of https://github.com/digint/btrbk
btrbk: add transaction log (configuration option "transaction_log <file>"
parent
282668927d
commit
46c7841e93
|
@ -12,6 +12,7 @@ btrbk-current
|
||||||
* Added "target raw", with GnuPG and compression support
|
* Added "target raw", with GnuPG and compression support
|
||||||
(experimental).
|
(experimental).
|
||||||
* Added configuration option "timestamp_format short|long".
|
* Added configuration option "timestamp_format short|long".
|
||||||
|
* Added transaction log (configuration option "transaction_log").
|
||||||
* Bugfix: correctly handle "incremental no" option.
|
* Bugfix: correctly handle "incremental no" option.
|
||||||
* Bugfix: return exit status 10 instead of 0 if one or more backup
|
* Bugfix: return exit status 10 instead of 0 if one or more backup
|
||||||
tasks aborted.
|
tasks aborted.
|
||||||
|
|
152
btrbk
152
btrbk
|
@ -45,6 +45,7 @@ use warnings FATAL => qw( all );
|
||||||
use Carp qw(confess);
|
use Carp qw(confess);
|
||||||
use Date::Calc qw(Today_and_Now Delta_Days Day_of_Week);
|
use Date::Calc qw(Today_and_Now Delta_Days Day_of_Week);
|
||||||
use Getopt::Long qw(GetOptions);
|
use Getopt::Long qw(GetOptions);
|
||||||
|
use POSIX qw(strftime);
|
||||||
use Data::Dumper;
|
use Data::Dumper;
|
||||||
|
|
||||||
our $VERSION = "0.21.0-dev";
|
our $VERSION = "0.21.0-dev";
|
||||||
|
@ -89,6 +90,7 @@ my %config_options = (
|
||||||
ssh_port => { default => "default", accept => [ "default" ], accept_numeric => 1 },
|
ssh_port => { default => "default", accept => [ "default" ], accept_numeric => 1 },
|
||||||
ssh_compression => { default => undef, accept => [ "yes", "no" ] },
|
ssh_compression => { default => undef, accept => [ "yes", "no" ] },
|
||||||
ssh_cipher_spec => { default => "default", accept_regexp => qr/^$ssh_cipher_match(,$ssh_cipher_match)*$/ },
|
ssh_cipher_spec => { default => "default", accept_regexp => qr/^$ssh_cipher_match(,$ssh_cipher_match)*$/ },
|
||||||
|
transaction_log => { default => undef, accept_file => { absolute => 1 } },
|
||||||
|
|
||||||
raw_target_compress => { default => undef, accept => [ "no", "gzip", "bzip2", "xz" ] },
|
raw_target_compress => { default => undef, accept => [ "no", "gzip", "bzip2", "xz" ] },
|
||||||
raw_target_encrypt => { default => undef, accept => [ "no", "gpg" ] },
|
raw_target_encrypt => { default => undef, accept => [ "no", "gpg" ] },
|
||||||
|
@ -146,8 +148,9 @@ my %table_formats = (
|
||||||
},
|
},
|
||||||
|
|
||||||
action_log => { table => [ qw( type status target source parent ) ],
|
action_log => { table => [ qw( type status target source parent ) ],
|
||||||
long => [ qw( localtime type status target_host target_path source_host source_path parent_path error_message ) ],
|
long => [ qw( localtime type status duration target_host target_path source_host source_path parent_path message ) ],
|
||||||
raw => [ qw( time localtime type status target_url source_url parent_url error_message ) ],
|
raw => [ qw( time localtime type status duration target_url source_url parent_url message ) ],
|
||||||
|
tlog => [ qw( localtime type status target_url source_url parent_url message ) ],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -162,7 +165,8 @@ my $dryrun;
|
||||||
my $loglevel = 1;
|
my $loglevel = 1;
|
||||||
my $show_progress = 0;
|
my $show_progress = 0;
|
||||||
my $err = "";
|
my $err = "";
|
||||||
my $output_format = undef;
|
my $output_format;
|
||||||
|
my $tlog_fh;
|
||||||
|
|
||||||
|
|
||||||
$SIG{__DIE__} = sub {
|
$SIG{__DIE__} = sub {
|
||||||
|
@ -214,29 +218,57 @@ sub INFO { my $t = shift; print STDERR "$t\n" if($loglevel >= 2); }
|
||||||
sub WARN { my $t = shift; print STDERR "WARNING: $t\n" if($loglevel >= 1); }
|
sub WARN { my $t = shift; print STDERR "WARNING: $t\n" if($loglevel >= 1); }
|
||||||
sub ERROR { my $t = shift; print STDERR "ERROR: $t\n"; }
|
sub ERROR { my $t = shift; print STDERR "ERROR: $t\n"; }
|
||||||
|
|
||||||
sub action($@)
|
|
||||||
{
|
|
||||||
my $type = shift // die;
|
|
||||||
my %h = @_;
|
|
||||||
$h{type} = $type;
|
|
||||||
$h{time} = time;
|
|
||||||
push @action_log, \%h;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub ABORTED($$)
|
sub ABORTED($$)
|
||||||
{
|
{
|
||||||
my $config = shift;
|
my $config = shift;
|
||||||
my $t = shift;
|
my $t = shift;
|
||||||
$config->{ABORTED} = $t;
|
$config->{ABORTED} = $t;
|
||||||
unless($t eq "USER_SKIP") {
|
unless($t eq "USER_SKIP") {
|
||||||
|
$t =~ s/\n/ /g;
|
||||||
action("abort_" . ($config->{CONTEXT} || "undef"),
|
action("abort_" . ($config->{CONTEXT} || "undef"),
|
||||||
status => "ABORT",
|
status => "ABORT",
|
||||||
vinfo_prefixed_keys("target", vinfo($config->{url}, $config)),
|
vinfo_prefixed_keys("target", vinfo($config->{url}, $config)),
|
||||||
error_message => $t,
|
message => $t,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub init_transaction_log($)
|
||||||
|
{
|
||||||
|
my $file = shift // die;
|
||||||
|
if(open($tlog_fh, ">> $file"))
|
||||||
|
{
|
||||||
|
# print headers and startup message
|
||||||
|
print_formatted("action_log", [ ], output_format => "tlog", outfile => $tlog_fh);
|
||||||
|
INFO "Using transaction log: $file";
|
||||||
|
} else {
|
||||||
|
$tlog_fh = undef;
|
||||||
|
ERROR "Failed to open transaction log '$file': $!";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub close_transaction_log()
|
||||||
|
{
|
||||||
|
if($tlog_fh) {
|
||||||
|
DEBUG "Closing transaction log";
|
||||||
|
close $tlog_fh || ERROR "Failed to close transaction log: $!";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub action($@)
|
||||||
|
{
|
||||||
|
my $type = shift // die;
|
||||||
|
my $h = { @_ };
|
||||||
|
my $time = time;
|
||||||
|
$h->{type} = $type;
|
||||||
|
$h->{time} = $time;
|
||||||
|
$h->{localtime} = strftime("%FT%T%z", localtime($time));
|
||||||
|
print_formatted("action_log", [ $h ], output_format => "tlog", no_header => 1, outfile => $tlog_fh);
|
||||||
|
push @action_log, $h;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
sub run_cmd(@)
|
sub run_cmd(@)
|
||||||
{
|
{
|
||||||
my @commands = (ref($_[0]) eq "HASH") ? @_ : { @_ };
|
my @commands = (ref($_[0]) eq "HASH") ? @_ : { @_ };
|
||||||
|
@ -1014,11 +1046,13 @@ sub btrfs_subvolume_snapshot($$)
|
||||||
DEBUG "[btrfs] source: $src_path";
|
DEBUG "[btrfs] source: $src_path";
|
||||||
DEBUG "[btrfs] target: $target_path";
|
DEBUG "[btrfs] target: $target_path";
|
||||||
INFO ">>> $target_vol->{PRINT}";
|
INFO ">>> $target_vol->{PRINT}";
|
||||||
|
my $starttime = time;
|
||||||
my $ret = run_cmd(cmd => [ qw(btrfs subvolume snapshot), '-r', $src_path, $target_path ],
|
my $ret = run_cmd(cmd => [ qw(btrfs subvolume snapshot), '-r', $src_path, $target_path ],
|
||||||
rsh => $svol->{RSH},
|
rsh => $svol->{RSH},
|
||||||
);
|
);
|
||||||
action("snapshot",
|
action("snapshot",
|
||||||
status => ($dryrun ? "DRYRUN" : (defined($ret) ? "success" : "ERROR")),
|
status => ($dryrun ? "DRYRUN" : (defined($ret) ? "success" : "ERROR")),
|
||||||
|
duration => (time - $starttime),
|
||||||
vinfo_prefixed_keys("target", $target_vol),
|
vinfo_prefixed_keys("target", $target_vol),
|
||||||
vinfo_prefixed_keys("source", $svol),
|
vinfo_prefixed_keys("source", $svol),
|
||||||
);
|
);
|
||||||
|
@ -1047,11 +1081,13 @@ sub btrfs_subvolume_delete($@)
|
||||||
my @options;
|
my @options;
|
||||||
@options = ("--commit-$commit") if($commit);
|
@options = ("--commit-$commit") if($commit);
|
||||||
my @target_paths = map( { $_->{PATH} } @$targets);
|
my @target_paths = map( { $_->{PATH} } @$targets);
|
||||||
|
my $starttime = time;
|
||||||
my $ret = run_cmd(cmd => [ qw(btrfs subvolume delete), @options, @target_paths ],
|
my $ret = run_cmd(cmd => [ qw(btrfs subvolume delete), @options, @target_paths ],
|
||||||
rsh => $rsh,
|
rsh => $rsh,
|
||||||
);
|
);
|
||||||
action($opts{type} // "delete",
|
action($opts{type} // "delete",
|
||||||
status => ($dryrun ? "DRYRUN" : (defined($ret) ? "success" : "ERROR")),
|
status => ($dryrun ? "DRYRUN" : (defined($ret) ? "success" : "ERROR")),
|
||||||
|
duration => (time - $starttime),
|
||||||
vinfo_prefixed_keys("target", $_),
|
vinfo_prefixed_keys("target", $_),
|
||||||
) foreach (@$targets);
|
) foreach (@$targets);
|
||||||
ERROR "Failed to delete btrfs subvolumes: " . join(' ', map( { $_->{PRINT} } @$targets)) unless(defined($ret));
|
ERROR "Failed to delete btrfs subvolumes: " . join(' ', map( { $_->{PRINT} } @$targets)) unless(defined($ret));
|
||||||
|
@ -1100,7 +1136,16 @@ sub btrfs_send_receive($$$$)
|
||||||
rsh => $target->{RSH},
|
rsh => $target->{RSH},
|
||||||
name => "btrfs receive",
|
name => "btrfs receive",
|
||||||
};
|
};
|
||||||
|
my $starttime = time;
|
||||||
my $ret = run_cmd(@cmd_pipe);
|
my $ret = run_cmd(@cmd_pipe);
|
||||||
|
action("send-receive",
|
||||||
|
status => ($dryrun ? "DRYRUN" : (defined($ret) ? "success" : "ERROR")),
|
||||||
|
duration => (time - $starttime),
|
||||||
|
vinfo_prefixed_keys("target", $vol_received),
|
||||||
|
vinfo_prefixed_keys("source", $snapshot),
|
||||||
|
vinfo_prefixed_keys("parent", $parent),
|
||||||
|
);
|
||||||
|
|
||||||
unless(defined($ret)) {
|
unless(defined($ret)) {
|
||||||
ERROR "Failed to send/receive btrfs subvolume: $snapshot->{PRINT} " . ($parent_path ? "[$parent_path]" : "") . " -> $target->{PRINT}";
|
ERROR "Failed to send/receive btrfs subvolume: $snapshot->{PRINT} " . ($parent_path ? "[$parent_path]" : "") . " -> $target->{PRINT}";
|
||||||
|
|
||||||
|
@ -1194,6 +1239,7 @@ sub btrfs_send_to_file($$$$;@)
|
||||||
DEBUG "[btrfs] parent: $parent->{PRINT}" if($parent);
|
DEBUG "[btrfs] parent: $parent->{PRINT}" if($parent);
|
||||||
DEBUG "[btrfs] target: $target->{PRINT}";
|
DEBUG "[btrfs] target: $target->{PRINT}";
|
||||||
|
|
||||||
|
my $starttime = time;
|
||||||
my $ret = run_cmd(@cmd_pipe);
|
my $ret = run_cmd(@cmd_pipe);
|
||||||
if(defined($ret)) {
|
if(defined($ret)) {
|
||||||
# Test target file for "exists and size > 0" after writing,
|
# Test target file for "exists and size > 0" after writing,
|
||||||
|
@ -1205,6 +1251,13 @@ sub btrfs_send_to_file($$$$;@)
|
||||||
name => "test",
|
name => "test",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
action("send-to-raw",
|
||||||
|
status => ($dryrun ? "DRYRUN" : (defined($ret) ? "success" : "ERROR")),
|
||||||
|
duration => (time - $starttime),
|
||||||
|
vinfo_prefixed_keys("target", $vol_received),
|
||||||
|
vinfo_prefixed_keys("source", $snapshot),
|
||||||
|
vinfo_prefixed_keys("parent", $parent),
|
||||||
|
);
|
||||||
unless(defined($ret)) {
|
unless(defined($ret)) {
|
||||||
ERROR "Failed to send btrfs subvolume to raw file: $snapshot->{PRINT} " . ($parent_path ? "[$parent_path]" : "") . " -> $vol_received->{PRINT}";
|
ERROR "Failed to send btrfs subvolume to raw file: $snapshot->{PRINT} " . ($parent_path ? "[$parent_path]" : "") . " -> $vol_received->{PRINT}";
|
||||||
return undef;
|
return undef;
|
||||||
|
@ -1438,12 +1491,6 @@ sub macro_send_receive($@)
|
||||||
$config_target->{SUBVOL_RECEIVED} //= [];
|
$config_target->{SUBVOL_RECEIVED} //= [];
|
||||||
push(@{$config_target->{SUBVOL_RECEIVED}}, \%info);
|
push(@{$config_target->{SUBVOL_RECEIVED}}, \%info);
|
||||||
|
|
||||||
action("send-receive",
|
|
||||||
status => ($dryrun ? "DRYRUN" : ($ret ? "success" : "ERROR")),
|
|
||||||
vinfo_prefixed_keys("target", $vol_received),
|
|
||||||
vinfo_prefixed_keys("source", $snapshot),
|
|
||||||
vinfo_prefixed_keys("parent", $parent),
|
|
||||||
);
|
|
||||||
unless($ret) {
|
unless($ret) {
|
||||||
$info{ERROR} = 1;
|
$info{ERROR} = 1;
|
||||||
return undef;
|
return undef;
|
||||||
|
@ -1818,6 +1865,18 @@ sub print_formatted(@)
|
||||||
print join(' ', map { "$_=\"" . ($row->{$_} // "") . "\""; } @$keys) . "\n";
|
print join(' ', map { "$_=\"" . ($row->{$_} // "") . "\""; } @$keys) . "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
elsif($format eq "tlog")
|
||||||
|
{
|
||||||
|
# output: value0 value1, ...
|
||||||
|
if($tlog_fh) {
|
||||||
|
unless($args{no_header}) {
|
||||||
|
print $tlog_fh join(' ', @$keys) . "\n";
|
||||||
|
}
|
||||||
|
foreach my $row (@$data) {
|
||||||
|
print $tlog_fh join(' ', map { ($row->{$_} // "-") } @$keys) . "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
# sanitize and calculate maxlen for each column
|
# sanitize and calculate maxlen for each column
|
||||||
|
@ -2136,6 +2195,15 @@ MAIN:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# open transaction log
|
||||||
|
#
|
||||||
|
if($action_run && (not $dryrun) && config_key($config, "transaction_log")) {
|
||||||
|
init_transaction_log(config_key($config, "transaction_log"));
|
||||||
|
}
|
||||||
|
action("startup", status => "v$VERSION", message => "$version_info");
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# filter subvolumes matching command line arguments
|
# filter subvolumes matching command line arguments
|
||||||
#
|
#
|
||||||
|
@ -3021,8 +3089,24 @@ MAIN:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
my $err_count = 0;
|
||||||
|
foreach my $config_vol (@{$config->{VOLUME}}) {
|
||||||
|
if($config_vol->{ABORTED} && ($config_vol->{ABORTED} ne "USER_SKIP")) { $err_count++; next; }
|
||||||
|
foreach my $config_subvol (@{$config_vol->{SUBVOLUME}}) {
|
||||||
|
if($config_subvol->{ABORTED} && ($config_subvol->{ABORTED} ne "USER_SKIP")) { $err_count++; next; }
|
||||||
|
foreach my $config_target (@{$config_subvol->{TARGET}}) {
|
||||||
|
if($config_target->{ABORTED} && ($config_target->{ABORTED} ne "USER_SKIP")) { $err_count++; next; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
my $time_elapsed = time - $start_time;
|
my $time_elapsed = time - $start_time;
|
||||||
INFO "Completed within: ${time_elapsed}s (" . localtime(time) . ")";
|
INFO "Completed within: ${time_elapsed}s (" . localtime(time) . ")";
|
||||||
|
action("finished",
|
||||||
|
duration => $time_elapsed,
|
||||||
|
message => $err_count ? "At least one backup task aborted" : "No errors"
|
||||||
|
);
|
||||||
|
close_transaction_log();
|
||||||
|
|
||||||
|
|
||||||
unless($quiet)
|
unless($quiet)
|
||||||
|
@ -3056,8 +3140,6 @@ MAIN:
|
||||||
{
|
{
|
||||||
my @unrecoverable;
|
my @unrecoverable;
|
||||||
my @out;
|
my @out;
|
||||||
my @raw_data;
|
|
||||||
my $err_count = 0;
|
|
||||||
foreach my $config_vol (@{$config->{VOLUME}})
|
foreach my $config_vol (@{$config->{VOLUME}})
|
||||||
{
|
{
|
||||||
my $sroot = $config_vol->{sroot} || vinfo($config_vol->{url}, $config_vol);
|
my $sroot = $config_vol->{sroot} || vinfo($config_vol->{url}, $config_vol);
|
||||||
|
@ -3096,18 +3178,18 @@ MAIN:
|
||||||
|
|
||||||
if($config_target->{ABORTED} && ($config_target->{ABORTED} ne "USER_SKIP")) {
|
if($config_target->{ABORTED} && ($config_target->{ABORTED} ne "USER_SKIP")) {
|
||||||
push @subvol_out, "!!! Target \"$droot->{PRINT}\" aborted: $config_target->{ABORTED}";
|
push @subvol_out, "!!! Target \"$droot->{PRINT}\" aborted: $config_target->{ABORTED}";
|
||||||
$err_count++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
push(@unrecoverable, $config_target->{UNRECOVERABLE}) if($config_target->{UNRECOVERABLE});
|
if($config_target->{UNRECOVERABLE}) {
|
||||||
|
push(@unrecoverable, $config_target->{UNRECOVERABLE});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if($config_vol->{ABORTED} && ($config_vol->{ABORTED} ne "USER_SKIP")) {
|
if($config_vol->{ABORTED} && ($config_vol->{ABORTED} ne "USER_SKIP")) {
|
||||||
# repeat volume errors in subvolume context ($err_count is increased in volume context below)
|
# repeat volume errors in subvolume context
|
||||||
push @subvol_out, "!!! Volume \"$sroot->{PRINT}\" aborted: $config_vol->{ABORTED}";
|
push @subvol_out, "!!! Volume \"$sroot->{PRINT}\" aborted: $config_vol->{ABORTED}";
|
||||||
}
|
}
|
||||||
if($config_subvol->{ABORTED} && ($config_subvol->{ABORTED} ne "USER_SKIP")) {
|
if($config_subvol->{ABORTED} && ($config_subvol->{ABORTED} ne "USER_SKIP")) {
|
||||||
push @subvol_out, "!!! Aborted: $config_subvol->{ABORTED}";
|
push @subvol_out, "!!! Aborted: $config_subvol->{ABORTED}";
|
||||||
$err_count++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(@subvol_out) {
|
if(@subvol_out) {
|
||||||
|
@ -3120,15 +3202,6 @@ MAIN:
|
||||||
push @out, "$svol->{PRINT}", "<no_action>", "";
|
push @out, "$svol->{PRINT}", "<no_action>", "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if($config_vol->{ABORTED} && ($config_vol->{ABORTED} ne "USER_SKIP")) {
|
|
||||||
push @raw_data, { type => "btrbk_volume",
|
|
||||||
status => "ABORT",
|
|
||||||
target_url => $sroot->{URL},
|
|
||||||
error_message => $config_vol->{ABORTED},
|
|
||||||
SORT => 1,
|
|
||||||
};
|
|
||||||
$err_count++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
print_header(title => "Backup Summary",
|
print_header(title => "Backup Summary",
|
||||||
|
@ -3164,20 +3237,11 @@ MAIN:
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
# print action log
|
# print action log
|
||||||
@action_log = map { $_->{localtime} = localtime($_->{time}); $_; } @action_log;
|
|
||||||
print_formatted("action_log", \@action_log, title => "BACKUP SUMMARY");
|
print_formatted("action_log", \@action_log, title => "BACKUP SUMMARY");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach my $config_vol (@{$config->{VOLUME}}) {
|
exit 10 if($err_count);
|
||||||
exit 10 if($config_vol->{ABORTED} && ($config_vol->{ABORTED} ne "USER_SKIP"));
|
|
||||||
foreach my $config_subvol (@{$config_vol->{SUBVOLUME}}) {
|
|
||||||
exit 10 if($config_subvol->{ABORTED} && ($config_subvol->{ABORTED} ne "USER_SKIP"));
|
|
||||||
foreach my $config_target (@{$config_subvol->{TARGET}}) {
|
|
||||||
exit 10 if($config_target->{ABORTED} && ($config_target->{ABORTED} ne "USER_SKIP"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,6 +64,9 @@ snapshot_dir _btrbk_snap
|
||||||
# Set this either globally or in a specific "target" section.
|
# Set this either globally or in a specific "target" section.
|
||||||
#btrfs_progs_compat no
|
#btrfs_progs_compat no
|
||||||
|
|
||||||
|
# Enable transaction log
|
||||||
|
#transaction_log /var/log/btrbk_transaction.log
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Volume section: "volume <volume-directory>"
|
# Volume section: "volume <volume-directory>"
|
||||||
|
|
|
@ -258,6 +258,13 @@ can lead to false guesses if the snapshot or target subvolumes are
|
||||||
manipulated by hand (moved, deleted).
|
manipulated by hand (moved, deleted).
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
|
\fBtransaction_log\fR <file>
|
||||||
|
.RS 4
|
||||||
|
If set, all transactions (snapshot create, subvolume send-receive,
|
||||||
|
subvolume delete) as well as abort messages are logged to <file>, in a
|
||||||
|
space-separated table format.
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
Lines that contain a hash character (#) in the first column are
|
Lines that contain a hash character (#) in the first column are
|
||||||
treated as comments.
|
treated as comments.
|
||||||
.SH AVAILABILITY
|
.SH AVAILABILITY
|
||||||
|
|
Loading…
Reference in New Issue