X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=application-src%2Fampgsql.pl;h=5c930ed323eaf099a202112a8945125de7c64474;hb=deb1d6847fe421fbab7d1ede23b5a1d8c83027a4;hp=1a20a242e5f2dc30a0d9f278dbb853e71162ea5f;hpb=fd48f3e498442f0cbff5f3606c7c403d0566150e;p=debian%2Famanda diff --git a/application-src/ampgsql.pl b/application-src/ampgsql.pl index 1a20a24..5c930ed 100644 --- a/application-src/ampgsql.pl +++ b/application-src/ampgsql.pl @@ -39,6 +39,7 @@ 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::MainLoop qw( :GIOCondition ); my $_DATA_DIR_TAR = "data_dir.tar"; my $_ARCHIVE_DIR_TAR = "archive_dir.tar"; @@ -89,10 +90,10 @@ sub new { $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; @@ -161,6 +162,7 @@ sub _check { 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}); @@ -170,11 +172,14 @@ sub _check_parent_dirs { 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 { @@ -204,12 +209,62 @@ sub _run_psql_command { push @cmd, '--quiet', '--output', '/dev/null', '--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; + debug("psql stdout: $line"); + 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 { @@ -243,62 +298,101 @@ sub command_selfcheck { 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) { + $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}); + } + } } } @@ -381,7 +475,7 @@ sub _run_tar_totals { $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"); } } @@ -448,7 +542,7 @@ sub _get_backup_info { # 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); @@ -633,13 +727,13 @@ sub _base_backup { '--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); } # create the final tar file - my $size = _run_tar_totals($self, '--directory', $tmp, + my $size = _run_tar_totals($self, '--directory', $tmp, '--file', '-', $_ARCHIVE_DIR_TAR, $_DATA_DIR_TAR); $self->{'state_cb'}->($self, $end_wal); @@ -676,11 +770,11 @@ sub _incr_backup { $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); } @@ -765,6 +859,8 @@ sub command_restore { 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)"); @@ -773,7 +869,7 @@ sub command_restore { 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; + $status = system($self->{'args'}->{'gnutar-path'}, '--extract', '--file', '-',) >> 8; (0 == $status) or die("Failed to extract base backup (exit status: $status)"); debug("extracting archive dir to $cur_dir/$_ARCHIVE_DIR_RESTORE"); @@ -807,7 +903,7 @@ 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",