2 * Copyright (c) 2008-2012 Zmanda, Inc. All Rights Reserved.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 * Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
19 * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
22 %module "Amanda::Tapelist"
23 %include "amglue/amglue.swg"
24 %include "exception.i"
26 %include "Amanda/Tapelist.pod"
33 use Amanda::Debug qw(:logging);
34 use Amanda::Config qw( config_dir_relative );
36 use Fcntl qw(:flock); # import LOCK_* constants
42 my ($filename, $lock ) = @_;
44 filename => $filename,
45 lockname => $filename . '.lock',
78 C_read_tapelist($self->{'filename'});
80 $self->_read_tapelist();
83 sub lookup_tapelabel {
87 for my $tle (@{$self->{'tles'}}) {
88 return $tle if ($tle->{'label'} eq $label);
98 $self->_update_positions();
99 return $self->{'tles'}->[$position-1];
102 sub lookup_tapedate {
104 my ($datestamp) = @_;
106 for my $tle (@{$self->{'tles'}}) {
107 return $tle if ($tle->{'datestamp'} eq $datestamp);
113 sub remove_tapelabel {
117 for (my $i = 0; $i < @{$self->{tles}}; $i++) {
118 if ($self->{tles}->[$i]->{'label'} eq $label) {
119 splice @{$self->{tles}}, $i, 1;
120 $self->_update_positions();
128 my ($datestamp, $label, $comment, $reuse, $meta, $barcode, $blocksize) = @_;
129 $reuse = 1 if !defined $reuse;
131 # prepend this (presumably new) volume to the beginning of the list
133 'datestamp' => $datestamp,
136 'barcode' => $barcode,
138 'blocksize' => $blocksize,
139 'comment' => $comment,
141 my $tles = $self->{'tles'};
142 if (!defined $tles->[0] ||
143 $tles->[0]->{'datestamp'} le $datestamp) {
144 unshift @{$tles}, $tle;
145 } elsif (defined $tles->[0] &&
146 $tles->[@$tles-1]->{'datestamp'} gt $datestamp) {
150 for my $i (0..(@$tles-1)) {
151 if ($tles->[$i]->{'datestamp'} le $datestamp) {
152 splice @{$tles}, $i, 0, $tle;
157 push @{$tles}, $tle if !$added;
159 $self->_update_positions();
166 $filename = $self->{'filename'} if !defined $filename;
168 my $new_tapelist_file = $filename . "-new-" . time();
170 open(my $fhn, ">", $new_tapelist_file) or die("Could not open '$new_tapelist_file' for writing: $!");
171 for my $tle (@{$self->{tles}}) {
172 my $datestamp = $tle->{'datestamp'};
173 my $label = $tle->{'label'};
174 my $reuse = $tle->{'reuse'} ? 'reuse' : 'no-reuse';
175 my $barcode = (defined $tle->{'barcode'})? (" BARCODE:" . $tle->{'barcode'}) : '';
176 my $meta = (defined $tle->{'meta'})? (" META:" . $tle->{'meta'}) : '';
177 my $blocksize = (defined $tle->{'blocksize'})? (" BLOCKSIZE:" . $tle->{'blocksize'}) : '';
178 my $comment = (defined $tle->{'comment'})? (" #" . $tle->{'comment'}) : '';
179 $result &&= print $fhn "$datestamp $label $reuse$barcode$meta$blocksize$comment\n";
181 my $result_close = close($fhn);
182 $result &&= $result_close;
184 return if (!$result);
186 unless (move($new_tapelist_file, $filename)) {
187 die ("failed to rename '$new_tapelist_file' to '$filename': $!");
190 # re-read from the C side to synchronize
191 C_read_tapelist($filename);
201 return if !exists $self->{'fl'};
203 $self->{'fl'}->unlock();
212 if (!-e $self->{'lockname'}) {
213 open(my $fhl, ">>", $self->{'lockname'});
216 my $fl = Amanda::Util::file_lock->new($self->{'lockname'});
217 while(($r = $fl->lock()) == 1) {
229 open(my $fh, "<", $self->{'filename'}) or return $self;
230 while (my $line = <$fh>) {
231 my ($datestamp, $label, $reuse, $barcode, $meta, $blocksize, $comment)
232 = $line =~ m/^([0-9]+)\s*([^\s]*)\s*(?:(reuse|no-reuse))?\s*(?:BARCODE:([^\s]*))?\s*(?:META:([^\s]*))?\s*(?:BLOCKSIZE:([^\s]*))?\s*(?:\#(.*))?$/mx;
233 if (!defined $datestamp) {
234 Amanda::Debug::critical("Bogus line in the tapelist ($self->{'filename'}) file: $line");
237 'datestamp' => $datestamp,
239 'reuse' => (!defined $reuse || $reuse eq 'reuse'),
240 'barcode' => $barcode,
242 'blocksize' => $blocksize,
243 'comment' => $comment,
248 # sort in descending order by datestamp, sorting on position, too, to ensure
249 # that entries with the same datestamp stay in the right order
250 $self->{'tles'} = \@tles;
251 $self->_update_positions();
253 $b->{'datestamp'} cmp $a->{'datestamp'}
254 || $a->{'position'} <=> $b->{'position'}
257 $self->{'tles'} = \@tles;
259 # and re-calculate the positions
260 $self->_update_positions(\@tles);
263 # update the 'position' key for each TLE
264 sub _update_positions {
266 my $tles = $self->{'tles'};
267 for (my $i = 0; $i < scalar @$tles; $i++) {
268 $tles->[$i]->{'position'} = $i+1;
274 char *get_last_reusable_tape_label(int skip);
275 %newobject list_new_tapes;
276 char *list_new_tapes(int nb);
278 /* C functions -- should be called *only* from within this module */
280 %rename(C_read_tapelist) read_tapelist;
281 int read_tapelist(char *tapefile);
283 %rename(C_clear_tapelist) clear_tapelist;
284 void clear_tapelist(void);