b514cac9020648b3ab2b217cdd07556b0fc484c0
[debian/amanda] / perl / Amanda / Tapelist.swg
1 /*
2  * Copyright (c) 2008, 2009, 2010 Zmanda, Inc.  All Rights Reserved.
3  *
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.
7  *
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
11  * for more details.
12  *
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
16  *
17  * Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
18  * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
19  */
20
21 %module "Amanda::Tapelist"
22 %include "amglue/amglue.swg"
23 %include "exception.i"
24
25 %include "Amanda/Tapelist.pod"
26
27 %{
28 #include "tapefile.h"
29 %}
30
31 %perlcode %{
32 use Amanda::Debug qw(:logging);
33 use Amanda::Config qw( config_dir_relative );
34 use File::Copy;
35 use Fcntl qw(:flock); # import LOCK_* constants
36
37 ## package functions
38
39 sub new {
40     my ($class)  = shift;
41     my ($filename, $lock ) = @_;
42     my $self = {
43         filename => $filename,
44         lockname => $filename . '.lock',
45     };
46     bless $self, $class;
47
48     $self->reload($lock);
49     return $self;
50 }
51
52 sub clear_tapelist {
53     my $self = shift;
54
55     # clear the C version
56     C_clear_tapelist();
57
58     $self->{'tles'} = [];
59
60     return $self;
61 }
62
63 ## methods
64
65 sub reload {
66     my $self = shift;
67     my ($lock) = @_;
68
69     if ($lock) {
70         $self->_take_lock();
71     }
72
73     # clear the C copy
74     C_clear_tapelist();
75
76     # let C read the file
77     C_read_tapelist($self->{'filename'});
78
79     $self->_read_tapelist();
80 }
81
82 sub lookup_tapelabel {
83     my $self = shift;
84     my ($label) = @_;
85
86     for my $tle (@{$self->{'tles'}}) {
87         return $tle if ($tle->{'label'} eq $label);
88     }
89
90     return undef;
91 }
92
93 sub lookup_tapepos {
94     my $self = shift;
95     my ($position) = @_;
96
97     $self->_update_positions();
98     return $self->{'tles'}->[$position-1];
99 }
100
101 sub lookup_tapedate {
102     my $self = shift;
103     my ($datestamp) = @_;
104
105     for my $tle (@{$self->{'tles'}}) {
106         return $tle if ($tle->{'datestamp'} eq $datestamp);
107     }
108
109     return undef;
110 }
111
112 sub remove_tapelabel {
113     my $self = shift;
114     my ($label) = @_;
115
116     for (my $i = 0; $i < @{$self->{tles}}; $i++) {
117         if ($self->{tles}->[$i]->{'label'} eq $label) {
118             splice @{$self->{tles}}, $i, 1;
119             $self->_update_positions();
120             return;
121         }
122     }
123 }
124
125 sub add_tapelabel {
126     my $self = shift;
127     my ($datestamp, $label, $comment, $reuse, $meta, $barcode, $blocksize) = @_;
128     $reuse = 1 if !defined $reuse;
129
130     # prepend this (presumably new) volume to the beginning of the list
131     my $tle = {
132         'datestamp' => $datestamp,
133         'label'     => $label,
134         'reuse'     => $reuse,
135         'barcode'   => $barcode,
136         'meta'      => $meta,
137         'blocksize' => $blocksize,
138         'comment'   => $comment,
139     };
140     my $tles = $self->{'tles'};
141     if (!defined $tles->[0] ||
142         $tles->[0]->{'datestamp'} le $datestamp) {
143         unshift @{$tles}, $tle;
144     } elsif (defined $tles->[0] &&
145         $tles->[@$tles-1]->{'datestamp'} gt $datestamp) {
146         push @{$tles}, $tle;
147     } else {
148         my $added = 0;
149         for my $i (0..(@$tles-1)) {
150             if ($tles->[$i]->{'datestamp'} le $datestamp) {
151                 splice @{$tles}, $i, 0, $tle;
152                 $added = 1;
153                 last;
154             }
155         }
156         push @{$tles}, $tle if !$added;
157     }
158     $self->_update_positions();
159 }
160
161 sub write {
162     my $self = shift;
163     my ($filename) = @_;
164     my $result = TRUE;
165     $filename = $self->{'filename'} if !defined $filename;
166
167     my $new_tapelist_file = $filename . "-new-" . time();
168
169     open(my $fhn, ">", $new_tapelist_file) or die("Could not open '$new_tapelist_file' for writing: $!");
170     for my $tle (@{$self->{tles}}) {
171         my $datestamp = $tle->{'datestamp'};
172         my $label = $tle->{'label'};
173         my $reuse = $tle->{'reuse'} ? 'reuse' : 'no-reuse';
174         my $barcode = (defined $tle->{'barcode'})? (" BARCODE:" . $tle->{'barcode'}) : '';
175         my $meta = (defined $tle->{'meta'})? (" META:" . $tle->{'meta'}) : '';
176         my $blocksize = (defined $tle->{'blocksize'})? (" BLOCKSIZE:" . $tle->{'blocksize'}) : '';
177         my $comment = (defined $tle->{'comment'})? (" #" . $tle->{'comment'}) : '';
178         $result &&= print $fhn "$datestamp $label $reuse$barcode$meta$blocksize$comment\n";
179     }
180     my $result_close = close($fhn);
181     $result &&= $result_close;
182
183     return if (!$result);
184
185     unless (move($new_tapelist_file, $filename)) {
186         die ("failed to rename '$new_tapelist_file' to '$filename': $!");
187     }
188
189     # re-read from the C side to synchronize
190     C_read_tapelist($filename);
191
192     $self->unlock();
193
194     return undef;
195 }
196
197 sub unlock {
198     my $self = shift;
199
200     return if !exists $self->{'fl'};
201
202     $self->{'fl'}->unlock();
203     delete $self->{'fl'}
204 }
205
206 ## private methods
207
208 sub _take_lock {
209     my $self = shift;
210
211     if (!-e $self->{'lockname'}) {
212         open(my $fhl, ">>", $self->{'lockname'});
213         close($fhl);
214     }
215     my $fl = Amanda::Util::file_lock->new($self->{'lockname'});
216     while(($r = $fl->lock()) == 1) {
217         sleep(1);
218     }
219     if ($r == 0) {
220         $self->{'fl'} = $fl;
221     }
222 }
223
224 sub _read_tapelist {
225     my $self = shift;
226
227     my @tles;
228     open(my $fh, "<", $self->{'filename'}) or return $self;
229     while (my $line = <$fh>) {
230         my ($datestamp, $label, $reuse, $barcode, $meta, $blocksize, $comment)
231             = $line =~ m/^([0-9]*)\s([^\s]*)\s(reuse|no-reuse)\s*(?:BARCODE:([^\s]*))?\s*(?:META:([^\s]*))?\s*(?:BLOCKSIZE:([^\s]*))?\s*(?:\#(.*))?$/mx;
232         next if !defined $datestamp; # silently filter out bogus lines
233         push @tles, {
234             'datestamp' => $datestamp,
235             'label' => $label,
236             'reuse' => ($reuse eq 'reuse'),
237             'barcode' => $barcode,
238             'meta' => $meta,
239             'blocksize' => $blocksize,
240             'comment' => $comment,
241         };
242     }
243     close($fh);
244
245     # sort in descending order by datestamp, sorting on position, too, to ensure
246     # that entries with the same datestamp stay in the right order
247     $self->{'tles'} = \@tles;
248     $self->_update_positions();
249     @tles = sort {
250            $b->{'datestamp'} cmp $a->{'datestamp'}
251         || $a->{'position'} <=> $b->{'position'}
252         } @tles;
253
254     $self->{'tles'} = \@tles;
255
256     # and re-calculate the positions
257     $self->_update_positions(\@tles);
258 }
259
260 # update the 'position' key for each TLE
261 sub _update_positions {
262     my $self = shift;
263     my $tles = $self->{'tles'};
264     for (my $i = 0; $i < scalar @$tles; $i++) {
265         $tles->[$i]->{'position'} = $i+1;
266     }
267 }
268
269 %}
270
271 char *get_last_reusable_tape_label(int skip);
272 char *list_new_tapes(int nb);
273
274 /* C functions -- should be called *only* from within this module */
275
276 %rename(C_read_tapelist) read_tapelist;
277 int read_tapelist(char *tapefile);
278
279 %rename(C_clear_tapelist) clear_tapelist;
280 void clear_tapelist(void);