2 # Copyright (c) 2005-2008 Zmanda Inc. All Rights Reserved.
4 # This program is free software; you can redistribute it and/or modify it
5 # under the terms of the GNU General Public License version 2 as published
6 # by the Free Software Foundation.
8 # This program is distributed in the hope that it will be useful, but
9 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 # You should have received a copy of the GNU General Public License along
14 # with this program; if not, write to the Free Software Foundation, Inc.,
15 # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 # Contact information: Zmanda Inc., 465 S Mathlida Ave, Suite 300
18 # Sunnyvale, CA 94086, USA, or: http://www.zmanda.com
20 use lib '@amperldir@';
24 package Amanda::Application::amgtar_perl;
25 use base qw(Amanda::Application);
31 use Amanda::Constants;
32 use Amanda::Config qw( :init :getconf config_dir_relative );
33 use Amanda::Debug qw( :logging );
35 use Amanda::Util qw( :constants );
39 my ($config, $host, $disk, $device, $level, $index, $message, $collection, $record, $calcsize) = @_;
40 my $self = $class->SUPER::new();
42 $self->{runtar} = ${Amanda::Paths::amlibexecdir} ."/runtar" .
44 $self->{gnulist} = $Amanda::Paths::GNUTAR_LISTED_INCREMENTAL_DIR;
45 $self->{gnutar} = $Amanda::Constants::GNUTAR;
47 $self->{config} = $config;
48 $self->{host} = $host;
49 $self->{disk} = $disk;
50 $self->{device} = $device;
51 $self->{level} = [ @{$level} ];
52 $self->{index} = $index;
53 $self->{message} = $message;
54 $self->{collection} = $collection;
55 $self->{record} = $record;
56 $self->{calcsize} = $calcsize;
67 print "MAX-LEVEL 9\n";
68 print "INDEX-LINE YES\n";
69 print "INDEX-XML NO\n";
70 print "MESSAGE-LINE YES\n";
71 print "MESSAGE-XML NO\n";
73 print "COLLECTION NO\n";
74 print "MULTI-ESTIMATE YES\n";
75 print "CALCSIZE YES\n";
78 sub command_selfcheck {
81 print "OK " . $self->{disk} . "\n";
82 print "OK " . $self->{device} . "\n";
86 #check property include/exclude
89 sub command_estimate {
92 if (defined $self->{calcsize}) {
93 $self->run_calcsize("GNUTAR", undef);
97 my($listdir) = $self->{'host'} . $self->{'disk'};
101 while (defined ($level = shift @{$self->{level}})) {
103 open($gnufile, ">$self->{gnulist}/${listdir}_${level}.new") || die();
104 close($gnufile) || die();
107 my($prev_level) = $level - 1;
108 if (-f "$self->{gnulist}/${listdir}_${prev_level}") {
109 copy("$self->{gnulist}/${listdir}_${prev_level}", "$self->{gnulist}/${listdir}_${level}.new");
111 open($gnufile, ">$self->{gnulist}/${listdir}_${level}.new") || die();
112 close($gnufile) || die();
113 #print "ERROR file $self->{gnulist}/${listdir}_${level}.new doesn't exist\n";
117 my(@cmd) = ($self->{runtar}, $self->{'config'}, $self->{'gnutar'}, "--create", "--directory", $self->{'device'}, "--listed-incremental", "$self->{gnulist}/${listdir}_${level}.new", "--sparse", "--one-file-system", "--ignore-failed-read", "--totals", "--file", "/dev/null", ".");
118 debug("cmd:" . join(" ", @cmd));
120 my $estimate_fd = Symbol::gensym;
121 my $pid = open3($wtrfh, '>&STDOUT', $estimate_fd, @cmd);
124 $size = parse_estimate($estimate_fd);
126 output_size($level, $size);
127 unlink "$self->{gnulist}/${listdir}_${level}.new";
137 if ($_ =~ /^Total bytes written: (\d*)/) {
149 print "$level -1 -1\n";
153 my($ksize) = int $size / (1024);
154 $ksize=32 if ($ksize<32);
155 print "$level $ksize 1\n";
162 my($listdir) = $self->{'host'} . $self->{'disk'};
164 $listdir =~ s/\//_/g;
165 my($level) = $self->{level}[0];
167 open(GNULIST, ">$self->{gnulist}/${listdir}_${level}.new") || die();
168 close(GNULIST) || die();
171 my($prev_level) = $level - 1;
172 copy("$self->{gnulist}/${listdir}_${prev_level}",
173 "$self->{gnulist}/${listdir}_${level}.new");
177 open($mesgout_fd, '>&=3') || die();
178 $self->{mesgout} = $mesgout_fd;
180 if(defined($self->{index})) {
181 $verbose = "--verbose";
183 my(@cmd) = ($self->{runtar}, $self->{config}, $self->{gnutar}, "--create", $verbose, "--directory", $self->{device}, "--listed-incremental", "$self->{gnulist}/${listdir}_${level}.new", "--sparse", "--one-file-system", "--ignore-failed-read", "--totals", "--file", "-", ".");
185 debug("cmd:" . join(" ", @cmd));
188 my $index_fd = Symbol::gensym;
189 my $pid = open3($wtrfh, '>&STDOUT', $index_fd, @cmd) || die();
192 if(defined($self->{index})) {
194 open($indexout_fd, '>&=4') || die();
195 $self->parse_backup($index_fd, $mesgout_fd, $indexout_fd);
199 $self->parse_backup($index_fd, $mesgout_fd, undef);
203 if(defined($self->{record})) {
204 debug("rename $self->{gnulist}/${listdir}_${level}.new $self->{gnulist}/${listdir}_${level}");
205 rename "$self->{gnulist}/${listdir}_${level}.new",
206 "$self->{gnulist}/${listdir}_${level}";
209 debug("unlink $self->{gnulist}/${listdir}_${level}.new");
210 unlink "$self->{gnulist}/${listdir}_${level}.new";
214 print $mesgout_fd "? $self->{gnutar} returned error\n";
222 my($fhin, $fhout, $indexout) = @_;
227 if(defined($indexout)) {
228 if(defined($self->{index})) {
235 if (/^Total bytes written: (\d*)/) {
237 $ksize = int ($size / 1024);
239 elsif(defined($fhout)) {
240 if (/: Directory is new$/ ||
241 /: Directory has been renamed/) {
249 if(defined($fhout)) {
253 my($ksize) = int ($size/1024);
254 print $fhout "sendbackup: size $ksize\n";
255 print $fhout "sendbackup: end\n";
260 sub command_index_from_output {
261 index_from_output(0, 1);
265 sub index_from_output {
266 my($fhin, $fhout) = @_;
269 next if /^Total bytes written:/;
276 sub command_index_from_image {
279 open($index_fd, "$self->{gnutar} --list --file - |") || die();
280 index_from_output($index_fd, 1);
283 sub command_restore {
286 chdir(Amanda::Util::get_original_cwd());
287 my(@cmd) = ($self->{gnutar}, "--numeric-owner", "-xpGvf", "-");
288 for(my $i=1;defined $ARGV[$i]; $i++) {
289 my $param = $ARGV[$i];
293 debug("cmd:" . join(" ", @cmd));
294 exec { $cmd[0] } @cmd;
295 die("Can't exec '", $cmd[0], "'");
298 sub command_validate {
301 my(@cmd) = ($self->{gnutar}, "-tf", "-");
302 debug("cmd:" . join(" ", @cmd));
303 my $pid = open3('>&STDIN', '>&STDOUT', '>&STDERR', @cmd) || die("validate", "Unable to run @cmd");
306 die("validate", "$self->{gnutar} returned error");
311 sub command_print_command {
318 Usage: amgtar_perl <command> --config=<config> --host=<host> --disk=<disk> --device=<device> --level=<level> --index=<yes|no> --message=<text> --collection=<no> --record=<yes|no> --calcsize.
334 Getopt::Long::Configure(qw{bundling});
336 'config=s' => \$opt_config,
337 'host=s' => \$opt_host,
338 'disk=s' => \$opt_disk,
339 'device=s' => \$opt_device,
340 'level=s' => \@opt_level,
341 'index=s' => \$opt_index,
342 'message=s' => \$opt_message,
343 'collection=s' => \$opt_collection,
344 'record' => \$opt_record,
345 'calcsize' => \$opt_calcsize,
348 my $application = Amanda::Application::amgtar_perl->new($opt_config, $opt_host, $opt_disk, $opt_device, \@opt_level, $opt_index, $opt_message, $opt_collection, $opt_record, $opt_calcsize);
350 $application->do($ARGV[0]);