use Amanda::Config qw( :init :getconf config_dir_relative string_to_boolean );
use Amanda::Debug qw( :logging );
use Amanda::Paths;
-use Amanda::Util qw( :constants :encoding );
+use Amanda::Util qw( :constants :encoding quote_string );
+use Amanda::MainLoop qw( :GIOCondition );
my $_DATA_DIR_TAR = "data_dir.tar";
my $_ARCHIVE_DIR_TAR = "archive_dir.tar";
$self->{'props'}->{$pname} = $conf_props->{$pname}->{'values'}->[0];
}
}
- # check for properties like 'foo-pg-host' where the device is 'foo'
- if ($self->{'args'}->{'device'}) {
+
+ # check for properties like 'foo-pg-host' where the diskname is 'foo'
+ if ($self->{'args'}->{'disk'}) {
foreach my $pname (@PROP_NAMES) {
- my $tmp = "$self->{'args'}->{'device'}-$pname";
+ my $tmp = "$self->{'args'}->{'disk'}-$pname";
if ($conf_props->{$tmp}) {
debug("More than one value for $tmp. Using the first.")
if scalar(@{$conf_props->{$tmp}->{'values'}}) > 1;
}
}
+ # overwrite with dumptype properties if they are set.
+ foreach my $pname (@PROP_NAMES) {
+ my $pdumpname = $pname;
+ $pdumpname =~ s/^pg-//g;
+ $self->{'props'}->{$pname} = $self->{'args'}->{$pdumpname}
+ if defined $self->{'args'}->{$pdumpname};
+debug("prop $pname set from dumpname $pdumpname: $self->{'args'}->{$pdumpname}")
+if defined $self->{'args'}->{$pdumpname};
+ }
+
unless ($self->{'props'}->{'psql-path'}) {
foreach my $pre (split(/:/, $ENV{PATH})) {
my $psql = "$pre/psql";
}
}
+ if (!exists $self->{'props'}->{'pg-datadir'}) {
+ $self->{'props'}->{'pg-datadir'} = $self->{'args'}->{'device'};
+ }
+
return $self;
}
sub _check_parent_dirs {
my ($dir) = @_;
+ my $ok = 1;
my $is_abs = substr($dir, 0, 1) eq "/";
_check("$dir is an absolute path?", "Yes", "No. It should start with '/'",
sub {$is_abs});
my $partial_path = '';
for my $path_part (@parts) {
$partial_path .= $path_part . (($partial_path || $is_abs)? '/' : '');
- _check("$partial_path is executable?", "Yes", "No",
+ $ok &&=
+ _check("$partial_path is executable?", "Yes", "No",
sub {-x $_[0]}, $partial_path);
- _check("$partial_path is a directory?", "Yes", "No",
+ $ok &&=
+ _check("$partial_path is a directory?", "Yes", "No",
sub {-d $_[0]}, $partial_path);
}
+ $ok;
}
sub _ok_passfile_perms {
push @cmd, "-p", $self->{'props'}->{'pg-port'} if ($self->{'props'}->{'pg-port'});
push @cmd, "-U", $self->{'props'}->{'pg-user'} if ($self->{'props'}->{'pg-user'});
- push @cmd, '--quiet', '--output', '/dev/null', '--command', $cmd, $self->{'props'}->{'pg-db'};
+ push @cmd, '--quiet', '--output', '/dev/null' if (!($cmd =~ /pg_xlogfile_name_offset/));
+ push @cmd, '--command', $cmd, $self->{'props'}->{'pg-db'};
debug("running " . join(" ", @cmd));
- my $status = system(@cmd);
+
+ my ($wtr, $rdr);
+ my $err = Symbol::gensym;
+ my $pid = open3($wtr, $rdr, $err, @cmd);
+ close($wtr);
+
+ my $file_to_close = 2;
+ my $psql_stdout_src = Amanda::MainLoop::fd_source($rdr,
+ $G_IO_IN|$G_IO_HUP|$G_IO_ERR);
+ my $psql_stderr_src = Amanda::MainLoop::fd_source($err,
+ $G_IO_IN|$G_IO_HUP|$G_IO_ERR);
+ $psql_stdout_src->set_callback(sub {
+ my $line = <$rdr>;
+ if (!defined $line) {
+ $file_to_close--;
+ $psql_stdout_src->remove();
+ Amanda::MainLoop::quit() if $file_to_close == 0;
+ return;
+ }
+ chomp $line;
+ return if $line =~ /^\s*$/;
+ debug("psql stdout: $line");
+ if ($cmd =~ /pg_xlogfile_name_offset/) {
+ return if $line =~ /file_name/;
+ return if $line =~ /------/;
+ return if $line =~ /\(1 row\)/;
+ if ($line =~ /^ ($_WAL_FILE_PAT)/) {
+ $self->{'switch_xlog_filename'} = $1;
+ return;
+ }
+ }
+ if ($line =~ /NOTICE: pg_stop_backup complete, all required WAL segments have been archived/) {
+ } else {
+ $self->print_to_server("psql stdout: $line",
+ $Amanda::Script_App::GOOD);
+ }
+ });
+ $psql_stderr_src->set_callback(sub {
+ my $line = <$err>;
+ if (!defined $line) {
+ $file_to_close--;
+ $psql_stderr_src->remove();
+ Amanda::MainLoop::quit() if $file_to_close == 0;
+ return;
+ }
+ chomp $line;
+ debug("psql stderr: $line");
+ if ($line =~ /NOTICE: pg_stop_backup complete, all required WAL segments have been archived/) {
+ } else {
+ $self->print_to_server("psql stderr: $line",
+ $Amanda::Script_App::GOOD);
+ }
+ });
+
+ close($wtr);
+ Amanda::MainLoop::run();
+ close($rdr);
+ close($err);
+
+ waitpid $pid, 0;
+ my $status = $?;
$ENV{'PGPASSWORD'} = $orig_pgpassword || '';
$ENV{'PGPASSFILE'} = $orig_pgpassfile || '';
- return 0 == ($status >>8)
+ return 0 == ($status >> 8)
}
sub command_selfcheck {
exit(1);
};
+ $self->print_to_server("disk " . quote_string($self->{args}->{disk}));
+
+ $self->print_to_server("ampgsql version " . $Amanda::Constants::VERSION,
+ $Amanda::Script_App::GOOD);
+
for my $k (keys %{$self->{'args'}}) {
print "OK application property: $k = $self->{'args'}->{$k}\n";
}
sub {!(-d $_[0])}, $Amanda::Constants::GNUTAR);
_check_parent_dirs($Amanda::Constants::GNUTAR);
- _check("TMPDIR $self->{'args'}->{'tmpdir'}",
+ _check("TMPDIR '$self->{'args'}->{'tmpdir'}'",
"is an acessible directory", "is NOT an acessible directory",
- sub {-d $_[0] && -r $_[0] && -w $_[0] && -x $_[0]},
+ sub {$_[0] && -d $_[0] && -r $_[0] && -w $_[0] && -x $_[0]},
$self->{'args'}->{'tmpdir'});
- _check("STATEDIR $self->{'args'}->{'statedir'}",
+
+ if (exists $self->{'props'}->{'pg-datadir'}) {
+ _check("PG-DATADIR property is",
+ "same as diskdevice", "differrent than diskdevice",
+ sub { $_[0] eq $_[1] },
+ $self->{'props'}->{'pg-datadir'}, $self->{'args'}->{'device'});
+ } else {
+ $self->{'props'}->{'pg-datadir'} = $self->{'args'}->{'device'};
+ }
+
+ _check("PG-DATADIR property", "is set", "is NOT set",
+ sub { $_[0] }, $self->{'props'}->{'pg-datadir'});
+ # note that the backup user need not be able ot read this dir
+
+ _check("STATEDIR '$self->{'args'}->{'statedir'}'",
"is an acessible directory", "is NOT an acessible directory",
- sub {-d $_[0] && -r $_[0] && -w $_[0] && -x $_[0]},
+ sub {$_[0] && -d $_[0] && -r $_[0] && -w $_[0] && -x $_[0]},
$self->{'args'}->{'statedir'});
_check_parent_dirs($self->{'args'}->{'statedir'});
if ($self->{'args'}->{'device'}) {
+ my $try_connect = 1;
+
for my $k (keys %{$self->{'props'}}) {
print "OK client property: $k = $self->{'props'}->{$k}\n";
}
- _check("PG-ARCHIVEDIR $self->{'props'}->{'pg-archivedir'}",
- "is a directory", "is NOT a directory",
- sub {-d $_[0]}, $self->{'props'}->{'pg-archivedir'});
- _check("PG-ARCHIVEDIR $self->{'props'}->{'pg-archivedir'}",
- "is readable", "is NOT readable",
- sub {-r $_[0]}, $self->{'props'}->{'pg-archivedir'});
- _check("PG-ARCHIVEDIR $self->{'props'}->{'pg-archivedir'}",
- "is executable", "is NOT executable",
- sub {-x $_[0]}, $self->{'props'}->{'pg-archivedir'});
- _check_parent_dirs($self->{'props'}->{'pg-archivedir'});
- _check("Are both PG-PASSFILE and PG-PASSWORD set?",
- "No (okay)",
- "Yes. Please set only one or the other",
- sub {!($self->{'props'}->{'pg-passfile'} and
- $self->{'props'}->{'pg-password'})});
+ if (_check("PG-ARCHIVEDIR property", "is set", "is NOT set",
+ sub { $_[0] }, $self->{'props'}->{'pg-archivedir'})) {
+ _check("PG-ARCHIVEDIR $self->{'props'}->{'pg-archivedir'}",
+ "is a directory", "is NOT a directory",
+ sub {-d $_[0]}, $self->{'props'}->{'pg-archivedir'});
+ _check("PG-ARCHIVEDIR $self->{'props'}->{'pg-archivedir'}",
+ "is readable", "is NOT readable",
+ sub {-r $_[0]}, $self->{'props'}->{'pg-archivedir'});
+ _check("PG-ARCHIVEDIR $self->{'props'}->{'pg-archivedir'}",
+ "is executable", "is NOT executable",
+ sub {-x $_[0]}, $self->{'props'}->{'pg-archivedir'});
+ _check_parent_dirs($self->{'props'}->{'pg-archivedir'});
+ }
+
+ $try_connect &&=
+ _check("Are both PG-PASSFILE and PG-PASSWORD set?",
+ "No (okay)",
+ "Yes. Please set only one or the other",
+ sub {!($self->{'props'}->{'pg-passfile'} and
+ $self->{'props'}->{'pg-password'})});
+
if ($self->{'props'}->{'pg-passfile'}) {
- _check("PG-PASSFILE $self->{'props'}->{'pg-passfile'}",
+ $try_connect &&=
+ _check("PG-PASSFILE $self->{'props'}->{'pg-passfile'}",
"has correct permissions", "does not have correct permissions",
\&_ok_passfile_perms, $self->{'props'}->{'pg-passfile'});
- _check_parent_dirs($self->{'props'}->{'pg-passfile'});
+ $try_connect &&=
+ _check_parent_dirs($self->{'props'}->{'pg-passfile'});
}
- _check("PSQL-PATH $self->{'props'}->{'psql-path'}",
- "is executable", "is NOT executable",
- sub {-x $_[0]}, $self->{'props'}->{'psql-path'});
- _check("PSQL-PATH $self->{'props'}->{'psql-path'}",
- "is not a directory (okay)", "is a directory (it shouldn't be)",
- sub {!(-d $_[0])}, $self->{'props'}->{'psql-path'});
- _check_parent_dirs($self->{'props'}->{'psql-path'});
- _check("Connecting to database server", "succeeded", "failed",
- \&_run_psql_command, $self, '');
+
+ if (_check("PSQL-PATH property", "is set", "is NOT set and psql is not in \$PATH",
+ sub { $_[0] }, $self->{'props'}->{'psql-path'})) {
+ $try_connect &&=
+ _check("PSQL-PATH $self->{'props'}->{'psql-path'}",
+ "is executable", "is NOT executable",
+ sub {-x $_[0]}, $self->{'props'}->{'psql-path'});
+ $try_connect &&=
+ _check("PSQL-PATH $self->{'props'}->{'psql-path'}",
+ "is not a directory (okay)", "is a directory (it shouldn't be)",
+ sub {!(-d $_[0])}, $self->{'props'}->{'psql-path'});
+ $try_connect &&=
+ _check_parent_dirs($self->{'props'}->{'psql-path'});
+ } else {
+ $try_connect = 0;
+ }
+
+ if ($try_connect) {
+ my @pv = `$self->{'props'}->{'psql-path'} --version`;
+ if ($? >> 8 == 0) {
+ $pv[0] =~ /^[^0-9]*([0-9.]*)[^0-9]*$/;
+ my $pv = $1;
+ $self->print_to_server("ampgsql psql-version $pv",
+ $Amanda::Script_App::GOOD);
+ } else {
+ $self->print_to_server(
+ "[Can't get " . $self->{'props'}->{'psql-path'} . " version]\n",
+ $Amanda::Script_App::ERROR);
+ }
+ }
+
+ if ($try_connect) {
+ $try_connect &&=
+ _check("Connecting to database server", "succeeded", "failed",
+ \&_run_psql_command, $self, '');
+ }
- my $label = "$self->{'label-prefix'}-selfcheck-" . time();
- if (_check("Call pg_start_backup", "succeeded",
- "failed (is another backup running?)",
- \&_run_psql_command, $self, "SELECT pg_start_backup('$label')")
- and _check("Call pg_stop_backup", "succeeded", "failed",
- \&_run_psql_command, $self, "SELECT pg_stop_backup()")) {
-
- _check("Get info from .backup file", "succeeded", "failed",
- sub {my ($start, $end) = _get_backup_info($self, $label); $start and $end});
- }
+ if ($try_connect) {
+ my $label = "$self->{'label-prefix'}-selfcheck-" . time();
+ if (_check("Call pg_start_backup", "succeeded",
+ "failed (is another backup running?)",
+ \&_run_psql_command, $self, "SELECT pg_start_backup('$label')")
+ and _check("Call pg_stop_backup", "succeeded", "failed",
+ \&_run_psql_command, $self, "SELECT pg_stop_backup()")) {
+
+ _check("Get info from .backup file", "succeeded", "failed",
+ sub {my ($start, $end) = _get_backup_info($self, $label); $start and $end});
+ }
+ }
+
+ {
+ my @gv = `$self->{'args'}->{'gnutar-path'} --version`;
+ if ($? >> 8 == 0) {
+ $gv[0] =~ /^[^0-9]*([0-9.]*)[^0-9]*$/;
+ my $gv = $1;
+ $self->print_to_server("ampgsql gtar-version $gv",
+ $Amanda::Script_App::GOOD);
+ } else {
+ $self->print_to_server(
+ "[Can't get " . $self->{'props'}->{'gnutar-path'} . " version]\n",
+ $Amanda::Script_App::ERROR);
+ }
+ }
}
}
my ($self, $level) = @_;
my @parts = ("ampgsql", hexencode($self->{'args'}->{'host'}), hexencode($self->{'args'}->{'disk'}), $level);
- $self->{'args'}->{'statedir'} . '/' . join("-", @parts);
+ my $statefile = $self->{'args'}->{'statedir'} . '/' . join("-", @parts);
+ debug("statefile: $statefile");
+ return $statefile;
}
sub _write_state_file {
$end_wal;
}
+sub _make_dummy_dir_base {
+ my ($self) = @_;
+
+ my $dummydir = "$self->{'args'}->{'tmpdir'}/ampgsql-dummy-$$";
+ mkpath("$dummydir/$_ARCHIVE_DIR_RESTORE");
+
+ return $dummydir;
+}
+
sub _make_dummy_dir {
my ($self) = @_;
sub _run_tar_totals {
my ($self, @other_args) = @_;
- my @cmd = ($self->{'runtar'}, $self->{'args'}->{'config'},
+ my @cmd;
+ @cmd = ($self->{'runtar'}, $self->{'args'}->{'config'},
$Amanda::Constants::GNUTAR, '--create', '--totals', @other_args);
- debug("running " . join(" ", @cmd));
+ debug("running: " . join(" ", @cmd));
local (*TAR_IN, *TAR_OUT, *TAR_ERR);
open TAR_OUT, ">&", $self->{'out_h'};
$size = $1;
} else {
chomp $l;
- #$self->print_to_server($l, $Amanda::Script_App::ERROR);
+ $self->print_to_server($l, $Amanda::Script_App::ERROR);
debug("TAR_ERR: $l");
}
}
# this works!)
local *TAROUT;
my $conf = $self->{'args'}->{'config'} || 'NOCONFIG';
- my $cmd = "$self->{'runtar'} $conf $Amanda::Constants::GNUTAR --create --directory $self->{'props'}->{'pg-archivedir'} $fname | $Amanda::Constants::GNUTAR --extract --to-stdout";
+ my $cmd = "$self->{'runtar'} $conf $Amanda::Constants::GNUTAR --create --file - --directory $self->{'props'}->{'pg-archivedir'} $fname | $Amanda::Constants::GNUTAR --file - --extract --to-stdout";
debug("running: $cmd");
open(TAROUT, "$cmd |");
my ($start, $end, $lab);
$lab = $1;
}
}
+ close TAROUT;
if ($lab and $lab eq $label) {
$start_wal = $start;
$end_wal = $end;
debug("$bfile named WALs $start_wal .. $end_wal");
# try to cleanup a bit, although this may fail and that's ok
- unlink("$self->{'props'}->{'pg-archivedir'}/$bfile");
+ my $filename = "$self->{'props'}->{'pg-archivedir'}/$bfile";
+ if (unlink($filename) == 0) {
+ debug("Failed to unlink '$filename': $!");
+ $self->print_to_server("Failed to unlink '$filename': $!",
+ $Amanda::Script_App::ERROR);
+ }
last;
}
sleep(1);
my $stoptime = time() + $maxwait;
while ($maxwait == 0 || time < $stoptime || $count++ < 4) {
return if -f "$archive_dir/$wal";
-
+
# for versions 8.0 or 8.1, the only way to "force" a WAL archive is to write
# garbage to the database.
- if ($pg_version < 802000) {
+ if ($pg_version < 80200) {
$self->_write_garbage_to_db();
} else {
sleep(1);
debug("running _base_backup");
my $label = "$self->{'label-prefix'}-" . time();
- my $tmp = "$self->{'args'}->{'tmpdir'}/$label";
-d $self->{'props'}->{'pg-archivedir'} or
die("WAL file archive directory does not exist (or is not a directory)");
- # try to protect what we create
- my $old_umask = umask();
- umask(077);
-
- my $cleanup = sub {
- umask($old_umask);
- eval {rmtree($tmp); 1}
- };
- my $old_die = $self->{'die_cb'};
- $self->{'die_cb'} = sub {
- my $msg = shift @_;
- $cleanup->();
- $old_die->($msg);
- };
- eval {rmtree($tmp,{'keep_root' => 1}); 1} or $self->{'die_cb'}->("Failed to clear tmp directory: $@");
- eval {mkpath($tmp, 0, 0700); 1} or $self->{'die_cb'}->("Failed to create tmp directory: $@");
-
_run_psql_command($self, "SELECT pg_start_backup('$label')") or
$self->{'die_cb'}->("Failed to call pg_start_backup");
}
$old_die_cb->($msg);
};
- _run_tar_totals($self, '--file', "$tmp/$_DATA_DIR_TAR",
+ my $size = _run_tar_totals($self, '--file', "-",
'--directory', $self->{'props'}->{'pg-datadir'},
'--exclude', 'postmaster.pid',
'--exclude', 'pg_xlog/*', # contains WAL files; will be handled below
+ '--transform', "s,^,$_DATA_DIR_RESTORE/,S",
".");
$self->{'die_cb'} = $old_die_cb;
$adir->close();
if (@wal_files) {
- _run_tar_totals($self, '--file', "$tmp/$_ARCHIVE_DIR_TAR",
- '--directory', $self->{'props'}->{'pg-archivedir'}, @wal_files);
+ $size += _run_tar_totals($self, '--file', "-",
+ '--directory', $self->{'props'}->{'pg-archivedir'},
+ '--transform', "s,^,$_ARCHIVE_DIR_RESTORE/,S",
+ @wal_files);
} else {
- my $dummydir = $self->_make_dummy_dir();
- $self->{'done_cb'}->(_run_tar_totals($self,
- '--directory', $dummydir, "empty-incremental"));
+ my $dummydir = $self->_make_dummy_dir_base();
+ $self->{'done_cb'}->(_run_tar_totals($self, '--file', '-',
+ '--directory', $dummydir, "$_ARCHIVE_DIR_RESTORE"));
rmtree($dummydir);
}
- # create the final tar file
- my $size = _run_tar_totals($self, '--directory', $tmp,
- $_ARCHIVE_DIR_TAR, $_DATA_DIR_TAR);
-
$self->{'state_cb'}->($self, $end_wal);
- $cleanup->();
$self->{'done_cb'}->($size);
}
debug("running _incr_backup");
+ if ($self->{'action'} eq 'backup') {
+ _run_psql_command($self, "SELECT file_name from pg_xlogfile_name_offset(pg_switch_xlog())");
+ if (defined($self->{'switch_xlog_filename'})) {
+ $self->_wait_for_wal($self->{'switch_xlog_filename'});
+ }
+ }
+
my $end_wal = _get_prev_state($self);
if ($end_wal) {
debug("previously ended at: $end_wal");
$self->{'state_cb'}->($self, $max_wal ? $max_wal : $end_wal);
if (@wal_files) {
- $self->{'done_cb'}->(_run_tar_totals($self,
+ $self->{'done_cb'}->(_run_tar_totals($self, '--file', '-',
'--directory', $self->{'props'}->{'pg-archivedir'}, @wal_files));
} else {
my $dummydir = $self->_make_dummy_dir();
- $self->{'done_cb'}->(_run_tar_totals($self,
+ $self->{'done_cb'}->(_run_tar_totals($self, '--file', '-',
'--directory', $dummydir, "empty-incremental"));
rmtree($dummydir);
}
$self->{'unlink_cb'} = sub {
my $filename = shift @_;
debug("unlinking WAL file $filename");
- unlink($filename);
+ if (unlink($filename) == 0) {
+ debug("Failed to unlink '$filename': $!");
+ $self->print_to_server("Failed to unlink '$filename': $!",
+ $Amanda::Script_App::ERROR);
+ }
};
} else {
$self->{'unlink_cb'} = sub {
}
sub command_restore {
- my $self = shift;
+ my $self = shift;
- chdir(Amanda::Util::get_original_cwd());
- if (defined $self->{'args'}->{directory}) {
- if (!-d $self->{'args'}->{directory}) {
- $self->print_to_server_and_die("Directory $self->{directory}: $!",
- $Amanda::Script_App::ERROR);
- }
- if (!-w $self->{'args'}->{directory}) {
- $self->print_to_server_and_die("Directory $self->{directory}: $!",
- $Amanda::Script_App::ERROR);
- }
- chdir($self->{'args'}->{directory});
- }
- my $cur_dir = POSIX::getcwd();
+ chdir(Amanda::Util::get_original_cwd());
+ if (defined $self->{'args'}->{directory}) {
+ if (!-d $self->{'args'}->{directory}) {
+ $self->print_to_server_and_die("Directory $self->{directory}: $!",
+ $Amanda::Script_App::ERROR);
+ }
+ if (!-w $self->{'args'}->{directory}) {
+ $self->print_to_server_and_die("Directory $self->{directory}: $!",
+ $Amanda::Script_App::ERROR);
+ }
+ chdir($self->{'args'}->{directory});
+ }
+ my $cur_dir = POSIX::getcwd();
- if (!-d $_ARCHIVE_DIR_RESTORE) {
- mkdir($_ARCHIVE_DIR_RESTORE) or die("could not create archive WAL directory: $!");
- }
- my $status;
- if ($self->{'args'}->{'level'} > 0) {
- debug("extracting incremental backup to $cur_dir/$_ARCHIVE_DIR_RESTORE");
- $status = system($self->{'args'}->{'gnutar-path'}, '--extract',
- '--exclude', 'empty-incremental',
- '--directory', $_ARCHIVE_DIR_RESTORE) >> 8;
- (0 == $status) or die("Failed to extract level $self->{'args'}->{'level'} backup (exit status: $status)");
- } else {
- debug("extracting base of full backup");
- if (!-d $_DATA_DIR_RESTORE) {
- mkdir($_DATA_DIR_RESTORE) or die("could not create archive WAL directory: $!");
- }
- $status = system($self->{'args'}->{'gnutar-path'}, '--extract') >> 8;
- (0 == $status) or die("Failed to extract base backup (exit status: $status)");
-
- debug("extracting archive dir to $cur_dir/$_ARCHIVE_DIR_RESTORE");
- $status = system($self->{'args'}->{'gnutar-path'}, '--extract',
- '--exclude', 'empty-incremental',
- '--file', $_ARCHIVE_DIR_TAR, '--directory', $_ARCHIVE_DIR_RESTORE) >> 8;
- (0 == $status) or die("Failed to extract archived WAL files from base backup (exit status: $status)");
- unlink($_ARCHIVE_DIR_TAR);
-
- debug("extracting data dir to $cur_dir/$_DATA_DIR_RESTORE");
- $status = system($self->{'args'}->{'gnutar-path'}, '--extract',
- '--file', $_DATA_DIR_TAR, '--directory', $_DATA_DIR_RESTORE) >> 8;
- (0 == $status) or die("Failed to extract data directory from base backup (exit status: $status)");
- unlink($_DATA_DIR_TAR);
- }
+ if (!-d $_ARCHIVE_DIR_RESTORE) {
+ mkdir($_ARCHIVE_DIR_RESTORE) or die("could not create archive WAL directory: $!");
+ }
+ my $status;
+ if ($self->{'args'}->{'level'} > 0) {
+ debug("extracting incremental backup to $cur_dir/$_ARCHIVE_DIR_RESTORE");
+ $status = system($self->{'args'}->{'gnutar-path'},
+ '--extract',
+ '--file', '-',
+ '--ignore-zeros',
+ '--exclude', 'empty-incremental',
+ '--directory', $_ARCHIVE_DIR_RESTORE) >> 8;
+ (0 == $status) or die("Failed to extract level $self->{'args'}->{'level'} backup (exit status: $status)");
+ } else {
+ debug("extracting base of full backup to $cur_dir/$_DATA_DIR_RESTORE");
+ debug("extracting archive dir to $cur_dir/$_ARCHIVE_DIR_RESTORE");
+ if (!-d $_DATA_DIR_RESTORE) {
+ mkdir($_DATA_DIR_RESTORE) or die("could not create archive WAL directory: $!");
+ }
+ my @cmd = ($self->{'args'}->{'gnutar-path'}, '--extract',
+ '--file', '-',
+ '--ignore-zero',
+ '--transform', "s,^DATA/,$_DATA_DIR_RESTORE/,S",
+ '--transform', "s,^WAL/,$_ARCHIVE_DIR_RESTORE/,S");
+ debug("run: " . join ' ',@cmd);
+ $status = system(@cmd) >> 8;
+ (0 == $status) or die("Failed to extract base backup (exit status: $status)");
+
+ if (-f $_ARCHIVE_DIR_TAR) {
+ debug("extracting archive dir to $cur_dir/$_ARCHIVE_DIR_RESTORE");
+ my @cmd = ($self->{'args'}->{'gnutar-path'}, '--extract',
+ '--exclude', 'empty-incremental',
+ '--file', $_ARCHIVE_DIR_TAR, '--directory',
+ $_ARCHIVE_DIR_RESTORE);
+ debug("run: " . join ' ',@cmd);
+ $status = system(@cmd) >> 8;
+ (0 == $status) or die("Failed to extract archived WAL files from base backup (exit status: $status)");
+ if (unlink($_ARCHIVE_DIR_TAR) == 0) {
+ debug("Failed to unlink '$_ARCHIVE_DIR_TAR': $!");
+ $self->print_to_server(
+ "Failed to unlink '$_ARCHIVE_DIR_TAR': $!",
+ $Amanda::Script_App::ERROR);
+ }
+ }
+
+ if (-f $_DATA_DIR_TAR) {
+ debug("extracting data dir to $cur_dir/$_DATA_DIR_RESTORE");
+ my @cmd = ($self->{'args'}->{'gnutar-path'}, '--extract',
+ '--file', $_DATA_DIR_TAR,
+ '--directory', $_DATA_DIR_RESTORE);
+ debug("run: " . join ' ',@cmd);
+ $status = system(@cmd) >> 8;
+ (0 == $status) or die("Failed to extract data directory from base backup (exit status: $status)");
+ if (unlink($_DATA_DIR_TAR) == 0) {
+ debug("Failed to unlink '$_DATA_DIR_TAR': $!");
+ $self->print_to_server("Failed to unlink '$_DATA_DIR_TAR': $!",
+ $Amanda::Script_App::ERROR);
+ }
+ }
+ }
}
sub command_validate {
return $self->default_validate();
}
- my(@cmd) = ($self->{'args'}->{'gnutar-path'}, "-tf", "-");
+ my(@cmd) = ($self->{'args'}->{'gnutar-path'}, "--ignore-zeros", "-tf", "-");
debug("cmd:" . join(" ", @cmd));
my $pid = open3('>&STDIN', '>&STDOUT', '>&STDERR', @cmd) ||
$self->print_to_server_and_die("Unable to run @cmd",
}
my $opts = {};
+my $opt_version;
GetOptions(
$opts,
+ 'version' => \$opt_version,
'config=s',
'host=s',
'disk=s',
'statedir=s',
'tmpdir=s',
'gnutar-path=s',
+ 'cleanupwal=s',
+ 'archivedir=s',
+ 'db=s',
+ 'host=s',
+ 'max-wal-wait=s',
+ 'passfile=s',
+ 'port=s',
+ 'user=s',
+ 'psql-path=s'
) or usage();
+if (defined $opt_version) {
+ print "ampgsql-" . $Amanda::Constants::VERSION , "\n";
+ exit(0);
+}
+
my $application = Amanda::Application::ampgsql->new($opts);
$application->do($ARGV[0]);