1# This file was automatically generated by SWIG (http://www.swig.org). 2# Version 3.0.7 3# 4# Do not make changes to this file unless you know what you are doing--modify 5# the SWIG interface file instead. 6 7package Amanda::Tapelist; 8use base qw(Exporter); 9use base qw(DynaLoader); 10package Amanda::Tapelistc; 11bootstrap Amanda::Tapelist; 12package Amanda::Tapelist; 13@EXPORT = qw(); 14 15# ---------- BASE METHODS ------------- 16 17package Amanda::Tapelist; 18 19sub TIEHASH { 20 my ($classname,$obj) = @_; 21 return bless $obj, $classname; 22} 23 24sub CLEAR { } 25 26sub FIRSTKEY { } 27 28sub NEXTKEY { } 29 30sub FETCH { 31 my ($self,$field) = @_; 32 my $member_func = "swig_${field}_get"; 33 $self->$member_func(); 34} 35 36sub STORE { 37 my ($self,$field,$newval) = @_; 38 my $member_func = "swig_${field}_set"; 39 $self->$member_func($newval); 40} 41 42sub this { 43 my $ptr = shift; 44 return tied(%$ptr); 45} 46 47 48# ------- FUNCTION WRAPPERS -------- 49 50package Amanda::Tapelist; 51 52*get_last_reusable_tape_label = *Amanda::Tapelistc::get_last_reusable_tape_label; 53*list_new_tapes = *Amanda::Tapelistc::list_new_tapes; 54*C_read_tapelist = *Amanda::Tapelistc::C_read_tapelist; 55*C_clear_tapelist = *Amanda::Tapelistc::C_clear_tapelist; 56 57# ------- VARIABLE STUBS -------- 58 59package Amanda::Tapelist; 60 61 62@EXPORT_OK = (); 63%EXPORT_TAGS = (); 64 65 66=head1 NAME 67 68Amanda::Tapelist - manipulate the Amanda tapelist 69 70=head1 SYNOPSIS 71 72 use Amanda::Tapelist; 73 74 # to get a read only copy of the tapelist file: 75 my $tl = Amanda::Tapelist->new("/path/to/tapefile"); 76 77 # to read/update/write the tapelist file 78 # read and take lock 79 my $tl = Amanda::Tapelist->new("/path/to/tapefile", 1); 80 # modify the memory copy 81 $tl->add_tapelabel($datestamp, $label); 82 $tl->add_tapelabel($datestamp2, $label2, $comment, 1); 83 # write it and unlock 84 $tl->write(); 85 86 # If you already have a read only copy and want to modify it 87 # take a read only copy 88 my $tl = Amanda::Tapelist->new("/path/to/tapefile"); 89 # reload and take lock 90 $tl->reload(1); 91 # modify the memory copy 92 tl->add_tapelabel($datestamp, $label); 93 $tl->add_tapelabel($datestamp2, $label2, $comment, 1); 94 # write it and unlock 95 $tl->write(); 96 97=head1 OBJECT-ORIENTED INTERFACE 98 99C<new> returns a hash with no C<tles> set if the tapelist does 100not exist. C<tles> is an empty array if the tapelist is empty. 101Invalid entries are silently ignored. 102 103=head2 tapelist object 104 105A tapelist object is a hash with the following keys: 106 107=over 108 109=item C<filename> 110 111 The filename of the tapelist file. 112 113=item C<filename_lock> 114 115 The filename of the lock file. 116 117=item C<fl> 118 119 A Amanda::Util::file_lock is the file is locked. 120 121=item C<tles> 122 123A sequence of tapelist elements (referred to as TLEs in this document), 124sorted by datestamp from newest to oldest. 125 126=back 127 128=head2 tapelist element 129 130A tapelist elementas a hash with the following keys: 131 132=over 133 134=item C<position> 135 136the one-based position of the TLE in the tapelist 137 138=item C<datestamp> 139 140the datestamp on which this was written, or "0" for an unused tape 141 142=item C<reuse> 143 144true if this tape can be reused when it is no longer active 145 146=item C<label> 147 148tape label 149 150=item C<comment> 151 152the comment for this tape, or undef if no comment was given 153 154=back 155 156=head1 Method 157 158The following methods are available on a tapelist object C<$tl>: 159 160=over 161 162=item C<relod($lock)> 163 164reload the tapelist file, lock it if $lock is set 165 166=item C<lookup_tapelabel($lbl)> 167 168look up and return a reference to the TLE with the given label 169 170=item C<lookup_tapepos($pos)> 171 172look up and return a reference to the TLE in the given position 173 174=item C<lookup_tapedate($date)> 175 176look up and return a reference to the TLE with the given datestamp 177 178=item C<remove_tapelabel($lbl)> 179 180remove the tape with the given label 181 182=item C<add_tapelabel($date, $lbl, $comment, $reuse)> 183 184add a tape with the given date, label, comment and reuse to the end of the 185tapelist. reuse can be 1 or undef for a reusable volume, it must be 0 for 186a no-reusable volume. 187 188=item C<write()> or C<write($filename)> 189 190write the tapelist out to the same file as when read or to C<$filename> if it 191is set, remove the lock if a lock was taken 192 193=item C<unlock()> 194 195remove the lock if a lock was taken 196 197=item C<clear_tapelist()> 198 199remove all tle from the tles. 200 201=back 202 203=head1 INTERACTION WITH C CODE 204 205The C portions of Amanda treat the tapelist as a global variable, 206while this package treats it as an object (and can thus handle more 207than one tapelist simultaneously). Every call to C<reload> 208fills this global variable with a copy of the tapelist, and likewise 209C<clear_tapelist> clears the global. However, any changes made from 210Perl are not reflected in the C copy, nor are changes made by C 211modules reflected in the Perl copy. 212 213=cut 214 215 216 217use Amanda::Debug qw(:logging); 218use Amanda::Config qw( config_dir_relative ); 219use File::Copy; 220use Fcntl qw(:flock); # import LOCK_* constants 221 222## package functions 223 224sub new { 225 my ($class) = shift; 226 my ($filename, $lock ) = @_; 227 my $self = { 228 filename => $filename, 229 lockname => $filename . '.lock', 230 }; 231 bless $self, $class; 232 233 $self->reload($lock); 234 return $self; 235} 236 237sub clear_tapelist { 238 my $self = shift; 239 240 # clear the C version 241 C_clear_tapelist(); 242 243 $self->{'tles'} = []; 244 245 return $self; 246} 247 248## methods 249 250sub reload { 251 my $self = shift; 252 my ($lock) = @_; 253 254 if ($lock) { 255 $self->_take_lock(); 256 } 257 258 # clear the C copy 259 C_clear_tapelist(); 260 261 # let C read the file 262 C_read_tapelist($self->{'filename'}); 263 264 $self->_read_tapelist(); 265} 266 267sub lookup_tapelabel { 268 my $self = shift; 269 my ($label) = @_; 270 271 for my $tle (@{$self->{'tles'}}) { 272 return $tle if ($tle->{'label'} eq $label); 273 } 274 275 return undef; 276} 277 278sub lookup_by_barcode { 279 my $self = shift; 280 my ($barcode) = @_; 281 282 for my $tle (@{$self->{'tles'}}) { 283 return $tle if (defined $tle->{'barcode'} and 284 $tle->{'barcode'} eq $barcode); 285 } 286 287 return undef; 288} 289 290sub lookup_tapepos { 291 my $self = shift; 292 my ($position) = @_; 293 294 $self->_update_positions(); 295 return $self->{'tles'}->[$position-1]; 296} 297 298sub lookup_tapedate { 299 my $self = shift; 300 my ($datestamp) = @_; 301 302 for my $tle (@{$self->{'tles'}}) { 303 return $tle if ($tle->{'datestamp'} eq $datestamp); 304 } 305 306 return undef; 307} 308 309sub remove_tapelabel { 310 my $self = shift; 311 my ($label) = @_; 312 313 for (my $i = 0; $i < @{$self->{tles}}; $i++) { 314 if ($self->{tles}->[$i]->{'label'} eq $label) { 315 splice @{$self->{tles}}, $i, 1; 316 $self->_update_positions(); 317 return; 318 } 319 } 320} 321 322sub add_tapelabel { 323 my $self = shift; 324 my ($datestamp, $label, $comment, $reuse, $meta, $barcode, $blocksize) = @_; 325 $reuse = 1 if !defined $reuse; 326 327 # prepend this (presumably new) volume to the beginning of the list 328 my $tle = { 329 'datestamp' => $datestamp, 330 'label' => $label, 331 'reuse' => $reuse, 332 'barcode' => $barcode, 333 'meta' => $meta, 334 'blocksize' => $blocksize, 335 'comment' => $comment, 336 }; 337 my $tles = $self->{'tles'}; 338 if (!defined $tles->[0] || 339 $tles->[0]->{'datestamp'} le $datestamp) { 340 unshift @{$tles}, $tle; 341 } elsif (defined $tles->[0] && 342 $tles->[@$tles-1]->{'datestamp'} gt $datestamp) { 343 push @{$tles}, $tle; 344 } else { 345 my $added = 0; 346 for my $i (0..(@$tles-1)) { 347 if ($tles->[$i]->{'datestamp'} le $datestamp) { 348 splice @{$tles}, $i, 0, $tle; 349 $added = 1; 350 last; 351 } 352 } 353 push @{$tles}, $tle if !$added; 354 } 355 $self->_update_positions(); 356} 357 358sub write { 359 my $self = shift; 360 my ($filename) = @_; 361 my $result = TRUE; 362 $filename = $self->{'filename'} if !defined $filename; 363 364 my $new_tapelist_file = $filename . "-new-" . time(); 365 366 open(my $fhn, ">", $new_tapelist_file) or die("Could not open '$new_tapelist_file' for writing: $!"); 367 for my $tle (@{$self->{tles}}) { 368 my $datestamp = $tle->{'datestamp'}; 369 my $label = $tle->{'label'}; 370 my $reuse = $tle->{'reuse'} ? 'reuse' : 'no-reuse'; 371 my $barcode = (defined $tle->{'barcode'})? (" BARCODE:" . $tle->{'barcode'}) : ''; 372 my $meta = (defined $tle->{'meta'})? (" META:" . $tle->{'meta'}) : ''; 373 my $blocksize = (defined $tle->{'blocksize'})? (" BLOCKSIZE:" . $tle->{'blocksize'}) : ''; 374 my $comment = (defined $tle->{'comment'})? (" #" . $tle->{'comment'}) : ''; 375 $result &&= print $fhn "$datestamp $label $reuse$barcode$meta$blocksize$comment\n"; 376 } 377 my $result_close = close($fhn); 378 $result &&= $result_close; 379 380 return if (!$result); 381 382 unless (move($new_tapelist_file, $filename)) { 383 die ("failed to rename '$new_tapelist_file' to '$filename': $!"); 384 } 385 386 # re-read from the C side to synchronize 387 C_read_tapelist($filename); 388 389 $self->unlock(); 390 391 return undef; 392} 393 394sub unlock { 395 my $self = shift; 396 397 return if !exists $self->{'fl'}; 398 399 $self->{'fl'}->unlock(); 400 delete $self->{'fl'} 401} 402 403## private methods 404 405sub _take_lock { 406 my $self = shift; 407 408 if (!-e $self->{'lockname'}) { 409 open(my $fhl, ">>", $self->{'lockname'}); 410 close($fhl); 411 } 412 my $fl = Amanda::Util::file_lock->new($self->{'lockname'}); 413 while(($r = $fl->lock()) == 1) { 414 sleep(1); 415 } 416 if ($r == 0) { 417 $self->{'fl'} = $fl; 418 } 419} 420 421sub _read_tapelist { 422 my $self = shift; 423 424 my @tles; 425 open(my $fh, "<", $self->{'filename'}) or return $self; 426 while (my $line = <$fh>) { 427 my ($datestamp, $label, $reuse, $barcode, $meta, $blocksize, $comment) 428 = $line =~ m/^([0-9]+)\s*([^\s]*)\s*(?:(reuse|no-reuse))?\s*(?:BARCODE:([^\s]*))?\s*(?:META:([^\s]*))?\s*(?:BLOCKSIZE:([^\s]*))?\s*(?:\#(.*))?$/mx; 429 if (!defined $datestamp) { 430 Amanda::Debug::critical("Bogus line in the tapelist ($self->{'filename'}) file: $line"); 431 } 432 push @tles, { 433 'datestamp' => $datestamp, 434 'label' => $label, 435 'reuse' => (!defined $reuse || $reuse eq 'reuse'), 436 'barcode' => $barcode, 437 'meta' => $meta, 438 'blocksize' => $blocksize, 439 'comment' => $comment, 440 }; 441 } 442 close($fh); 443 444 # sort in descending order by datestamp, sorting on position, too, to ensure 445 # that entries with the same datestamp stay in the right order 446 $self->{'tles'} = \@tles; 447 $self->_update_positions(); 448 @tles = sort { 449 $b->{'datestamp'} cmp $a->{'datestamp'} 450 || $a->{'position'} <=> $b->{'position'} 451 } @tles; 452 453 $self->{'tles'} = \@tles; 454 455 # and re-calculate the positions 456 $self->_update_positions(\@tles); 457 458 # check for duplicate labels 459 my %labels; 460 for my $tle (@{$self->{'tles'}}) { 461 my $label = $tle->{'label'}; 462 if (exists $labels{$label}) { 463 debug("Duplicate label '$label' in tapelist file."); 464 die("ERROR: Duplicate label '$label' in tapelist file."); 465 } 466 $labels{$label} = 1; 467 } 468} 469 470# update the 'position' key for each TLE 471sub _update_positions { 472 my $self = shift; 473 my $tles = $self->{'tles'}; 474 for (my $i = 0; $i < scalar @$tles; $i++) { 475 $tles->[$i]->{'position'} = $i+1; 476 } 477} 478 4791; 480