2 # Copyright (c) 2009-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 94086, USA, or: http://www.zmanda.com
21 use lib '@amperldir@';
29 use Amanda::Device qw( :constants );
30 use Amanda::Debug qw( :logging );
31 use Amanda::Config qw( :init :getconf config_dir_relative );
32 use Amanda::Util qw( :constants );
34 use Amanda::Header qw( :constants );
46 print STDERR "Usage: amlabel [--barcode <barcode>] [--meta <meta>] [--assign] [--version]\n"
47 . " [-f] [-o configoption]* <conf> [<label>] [slot <slot-number>]\n";
51 Amanda::Util::setup_application("amlabel", "server", $CONTEXT_CMDLINE);
53 my $config_overrides = new_config_overrides($#ARGV+1);
54 my ($opt_force, $opt_config, $opt_slot, $opt_label);
55 my ($opt_barcode, $opt_meta, $opt_assign);
62 debug("Arguments: " . join(' ', @ARGV));
63 Getopt::Long::Configure(qw(bundling));
65 'version' => \&Amanda::Util::version_opt,
66 'help|usage|?' => \&usage,
67 'o=s' => sub { add_config_override_opt($config_overrides, $_[1]); },
69 'barcode=s' => \$opt_barcode,
70 'meta=s' => \$opt_meta,
71 'assign' => \$opt_assign,
72 'version' => \&Amanda::Util::version_opt,
75 if ($opt_assign && (!$opt_meta and !$opt_barcode)) {
76 print STDERR "--assign require --barcode or --meta\n";
80 usage() if @ARGV == 0;
81 $opt_config = $ARGV[0];
85 } elsif (@ARGV == 2) {
87 $opt_label = $ARGV[1];
88 } elsif (@ARGV == 3 and $ARGV[1] eq 'slot') {
91 } elsif (@ARGV == 4 and $ARGV[2] eq 'slot') {
93 $opt_label = $ARGV[1];
98 set_config_overrides($config_overrides);
99 config_init($CONFIG_INIT_EXPLICIT_NAME, $opt_config);
100 my ($cfgerr_level, @cfgerr_errors) = config_errors();
101 if ($cfgerr_level >= $CFGERR_WARNINGS) {
102 config_print_errors();
103 if ($cfgerr_level >= $CFGERR_ERRORS) {
104 print STDERR "errors processing config file";
109 Amanda::Util::finish_setup($RUNNING_AS_DUMPUSER);
111 my ($tlf, $tl, $res);
114 my ($msg, $finished_cb) = @_;
115 print STDERR "$msg\n";
118 $res->release(finished_cb => sub {
128 my ($finished_cb) = @_;
134 my $steps = define_steps
135 cb_ref => \$finished_cb,
136 finalize => sub { $chg->quit() if defined $chg };
139 my $labelstr = getconf($CNF_LABELSTR);
140 if (defined ($opt_label) && $opt_label !~ /$labelstr/) {
141 return failure("Label '$opt_label' doesn't match labelstr '$labelstr'.", $finished_cb);
144 $tlf = Amanda::Config::config_dir_relative(getconf($CNF_TAPELIST));
145 $tl = Amanda::Tapelist->new($tlf);
147 return failure("Can't load tapelist file ($tlf)", $finished_cb);
150 $chg = Amanda::Changer->new(undef, tapelist => $tl);
152 return failure($chg, $finished_cb)
153 if $chg->isa("Amanda::Changer::Error");
156 return $steps->{'assign'}->();
159 if (defined($opt_label) && !$opt_force) {
160 if ($tl->lookup_tapelabel($opt_label)) {
161 return failure("Label '$opt_label' already on a volume", $finished_cb);
165 $steps->{'load'}->();
169 print "Reading label...\n";
171 $chg->load(slot => $opt_slot, mode => "write",
172 res_cb => $steps->{'loaded'});
173 } elsif ($opt_barcode) {
174 $chg->inventory(inventory_cb => $steps->{'inventory'});
176 $chg->load(relative_slot => "current", mode => "write",
177 res_cb => $steps->{'loaded'});
181 step inventory => sub {
182 my ($err, $inv) = @_;
184 return failure($err, $finished_cb) if $err;
187 if ($sl->{'barcode'} eq $opt_barcode) {
188 return $chg->load(slot => $sl->{'slot'}, mode => "write",
189 res_cb => $steps->{'loaded'});
193 return failure("No volume with barcode '$opt_barcode' available", $finished_cb);
197 (my $err, $res) = @_;
199 return failure($err, $finished_cb) if $err;
201 if (defined $opt_slot && defined $opt_barcode &&
202 $opt_barcode ne $res->{'barcode'}) {
203 if (defined $res->{'barcode'}) {
204 return failure("Volume in slot $opt_slot have barcode '$res->{'barcode'}, it is not '$opt_barcode'", $finished_cb);
206 return failure("Volume in slot $opt_slot have no barcode", $finished_cb);
209 $dev = $res->{'device'};
211 if ($dev->status & $DEVICE_STATUS_VOLUME_UNLABELED) {
212 if (!$dev->volume_header or $dev->volume_header->{'type'} == $F_EMPTY) {
213 print "Found an empty tape.\n";
215 # force is required for non-Amanda tapes
216 print "Found a non-Amanda tape.\n";
217 $dev_ok = 0 unless ($opt_force);
219 } elsif ($dev->status & $DEVICE_STATUS_VOLUME_ERROR) {
220 # it's OK to force through VOLUME_ERROR
221 print "Error reading volume label: " . $dev->error_or_status(), "\n";
222 $dev_ok = 0 unless ($opt_force);
223 } elsif ($dev->status != $DEVICE_STATUS_SUCCESS) {
224 # but anything else is fatal
225 print "Error reading volume label: " . $dev->error_or_status(), "\n";
228 # this is a labeled Amanda tape
229 my $label = $dev->volume_label;
230 my $labelstr = getconf($CNF_LABELSTR);
232 if ($label !~ /$labelstr/) {
233 print "Found label '$label', but it is not from configuration " .
234 "'" . Amanda::Config::get_config_name() . "'.\n";
235 $dev_ok = 0 unless ($opt_force);
236 } elsif ($tl->lookup_tapelabel($label)) {
237 print "Volume with label '$label' is active and contains data from this configuration.\n";
239 # if -f, then the user should clean things up..
240 print "Consider using 'amrmtape' to remove volume '$label' from the catalog.\n";
241 # note that we don't run amrmtape automatically, as it could result in data loss when
242 # multiple volumes have (perhaps accidentally) the same label
247 print "Found Amanda volume '$label'.\n";
251 $res->get_meta_label(finished_cb => $steps->{'got_meta'});
254 step got_meta => sub {
255 my ($err, $meta) = @_;
257 if (defined $meta && defined $opt_meta && $meta ne $opt_meta) {
260 $meta = $opt_meta if !defined $meta;
261 ($meta, my $merr) = $res->make_new_meta_label() if !defined $meta;
263 return failure($merr, $finished_cb);
267 my $label = $opt_label;
268 if (!defined($label)) {
269 ($label, my $lerr) = $res->make_new_tape_label(meta => $meta);
271 return failure($lerr, $finished_cb);
276 print "Writing label '$label'...\n";
278 if (!$dev->start($ACCESS_WRITE, $label, "X")) {
279 return failure("Error writing label: " . $dev->error_or_status(), $finished_cb);
280 } elsif (!$dev->finish()) {
281 return failure("Error finishing device: " . $dev->error_or_status(), $finished_cb);
284 print "Checking label...\n";
285 my $status = $dev->read_label();
286 if ($status != $DEVICE_STATUS_SUCCESS) {
287 return failure("Checking the tape label failed: " . $dev->error_or_status(),
289 } elsif (!$dev->volume_label) {
290 return failure("No label found.", $finished_cb);
291 } elsif ($dev->volume_label ne $label) {
292 my $got = $dev->volume_label;
293 return failure("Read back a different label: got '$got', but expected '$label'",
295 } elsif ($dev->volume_time ne "X") {
296 my $got = $dev->volume_time;
297 return failure("Read back a different timetstamp: got '$got', but expected 'X'",
301 # update the tapelist
303 $tl->remove_tapelabel($label);
304 $tl->add_tapelabel("0", $label, undef, 1, $meta, $res->{'barcode'}, $dev->block_size/1024);
310 $res->set_label(label => $label, finished_cb => $steps->{'set_meta_label'});
312 return failure("Not writing label.", $finished_cb);
316 step set_meta_label => sub {
320 return $res->set_meta_label(meta => $opt_meta,
321 finished_cb => $steps->{'labeled'});
323 return $steps->{'labeled'}->();
327 step labeled => sub {
329 $gerr = $err if !$gerr;
331 $res->release(finished_cb => $steps->{'released'});
334 step released => sub {
336 return failure($gerr, $finished_cb) if $gerr;
337 return failure($err, $finished_cb) if $err;
344 $tle = $tl->lookup_tapelabel($opt_label);
346 my $meta = $opt_meta;
348 if (defined($tle->{'meta'}) && $meta ne $tle->{'meta'} &&
350 return failure("Can't change meta-label with --force, old meta-label is '$tle->{'meta'}'");
353 $meta = $tle->{'meta'};
355 my $barcode = $opt_barcode;
356 if (defined $barcode) {
357 if (defined($tle->{'barcode'}) &&
358 $barcode ne $tle->{'barcode'} &&
360 return failure("Can't change barcode with --force, old barcode is '$tle->{'barcode'}'");
363 $barcode = $tle->{'barcode'};
367 $tl->remove_tapelabel($opt_label);
368 $tl->add_tapelabel($tle->{'datestamp'}, $tle->{'label'},
369 $tle->{'comment'}, $tle->{'reuse'}, $meta,
373 return failure("Label '$opt_label' is not in the tapelist file", $finished_cb);
376 $chg->inventory(inventory_cb => $steps->{'assign_inventory'});
379 step assign_inventory => sub {
380 my ($err, $inv) = @_;
383 return $finished_cb->() if $err->notimpl;
384 return failure($err, $finished_cb);
388 if (defined $sl->{'label'} && $sl->{'label'} eq $opt_label) {
389 return $chg->set_meta_label(meta => $opt_meta,
390 slot => $sl->{'slot'},
391 finished_cb => $steps->{'done'});
402 main(\&Amanda::MainLoop::quit);
403 Amanda::MainLoop::run();
404 Amanda::Util::finish_application();