2 # Copyright (c) 2009, 2010 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. Mathilda Ave., Suite 300
18 # Sunnyvale, CA 94086, USA, or: http://www.zmanda.com
20 use lib '@amperldir@';
28 use Amanda::Device qw( :constants );
29 use Amanda::Debug qw( :logging );
30 use Amanda::Config qw( :init :getconf config_dir_relative );
31 use Amanda::Util qw( :constants );
33 use Amanda::Header qw( :constants );
45 print STDERR "Usage: amlabel [-f] [-o configoption]* <conf> <label> [slot <slot-number>]\n";
49 Amanda::Util::setup_application("amlabel", "server", $CONTEXT_CMDLINE);
51 my $config_overrides = new_config_overrides($#ARGV+1);
52 my ($opt_force, $opt_config, $opt_slot, $opt_label);
55 Getopt::Long::Configure(qw(bundling));
57 'help|usage|?' => \&usage,
58 'o=s' => sub { add_config_override_opt($config_overrides, $_[1]); },
60 'version' => \&Amanda::Util::version_opt,
65 } elsif (@ARGV == 4 and $ARGV[2] eq 'slot') {
71 $opt_config = $ARGV[0];
72 $opt_label = $ARGV[1];
74 set_config_overrides($config_overrides);
75 config_init($CONFIG_INIT_EXPLICIT_NAME, $opt_config);
76 my ($cfgerr_level, @cfgerr_errors) = config_errors();
77 if ($cfgerr_level >= $CFGERR_WARNINGS) {
78 config_print_errors();
79 if ($cfgerr_level >= $CFGERR_ERRORS) {
80 print STDERR "errors processing config file";
85 Amanda::Util::finish_setup($RUNNING_AS_DUMPUSER);
90 my ($msg, $finished_cb) = @_;
91 print STDERR "$msg\n";
94 $res->release(finished_cb => sub {
104 my ($finished_cb) = @_;
106 my $steps = define_steps
107 cb_ref => \$finished_cb;
110 my $labelstr = getconf($CNF_LABELSTR);
111 if ($opt_label !~ /$labelstr/) {
112 return failure("Label '$opt_label' doesn't match labelstr '$labelstr'.", $finished_cb);
115 $tlf = Amanda::Config::config_dir_relative(getconf($CNF_TAPELIST));
116 $tl = Amanda::Tapelist->new($tlf);
118 return failure("Can't load tapelist file ($tlf)", $finished_cb);
121 if ($tl->lookup_tapelabel($opt_label)) {
122 return failure("Label '$opt_label' already on a volume", $finished_cb);
126 $steps->{'load'}->();
130 my $chg = Amanda::Changer->new();
132 return failure($chg, $finished_cb)
133 if $chg->isa("Amanda::Changer::Error");
135 print "Reading label...\n";
137 $chg->load(slot => $opt_slot, mode => "write",
138 res_cb => $steps->{'loaded'});
140 $chg->load(relative_slot => "current", mode => "write",
141 res_cb => $steps->{'loaded'});
146 (my $err, $res) = @_;
148 return failure($err, $finished_cb) if $err;
150 my $dev = $res->{'device'};
152 if ($dev->status & $DEVICE_STATUS_VOLUME_UNLABELED) {
153 if (!$dev->volume_header or $dev->volume_header->{'type'} == $F_EMPTY) {
154 print "Found an empty tape.\n";
156 # force is required for non-Amanda tapes
157 print "Found a non-Amanda tape.\n";
158 $dev_ok = 0 unless ($opt_force);
160 } elsif ($dev->status & $DEVICE_STATUS_VOLUME_ERROR) {
161 # it's OK to force through VOLUME_ERROR
162 print "Error reading volume label: " . $dev->error_or_status(), "\n";
163 $dev_ok = 0 unless ($opt_force);
164 } elsif ($dev->status != $DEVICE_STATUS_SUCCESS) {
165 # but anything else is fatal
166 print "Error reading volume label: " . $dev->error_or_status(), "\n";
169 # this is a labeled Amanda tape
170 my $label = $dev->volume_label;
171 my $labelstr = getconf($CNF_LABELSTR);
173 if ($label !~ /$labelstr/) {
174 print "Found label '$label', but it is not from configuration " .
175 "'" . Amanda::Config::get_config_name() . "'.\n";
176 $dev_ok = 0 unless ($opt_force);
177 } elsif ($tl->lookup_tapelabel($label)) {
178 print "Volume with label '$label' is active and contains data from this configuration.\n";
180 # if -f, then the user should clean things up..
181 print "Consider using 'amrmtape' to remove volume '$label' from the catalog.\n";
182 # note that we don't run amrmtape automatically, as it could result in data loss when
183 # multiple volumes have (perhaps accidentally) the same label
188 print "Found Amanda volume '$label'.\n";
193 print "Writing label '$opt_label'...\n";
195 if (!$dev->start($ACCESS_WRITE, $opt_label, "X")) {
196 return failure("Error writing label: " . $dev->error_or_status(), $finished_cb);
197 } elsif (!$dev->finish()) {
198 return failure("Error finishing device: " . $dev->error_or_status(), $finished_cb);
201 print "Checking label...\n";
202 my $status = $dev->read_label();
203 if ($status != $DEVICE_STATUS_SUCCESS) {
204 return failure("Checking the tape label failed: " . $dev->error_or_status(),
206 } elsif (!$dev->volume_label) {
207 return failure("No label found.", $finished_cb);
208 } elsif ($dev->volume_label ne $opt_label) {
209 my $got = $dev->volume_label;
210 return failure("Read back a different label: got '$got', but expected '$opt_label'",
212 } elsif ($dev->volume_time ne "X") {
213 my $got = $dev->volume_time;
214 return failure("Read back a different timetstamp: got '$got', but expected 'X'",
218 # update the tapelist
220 $tl->remove_tapelabel($opt_label);
221 $tl->add_tapelabel("0", $opt_label, undef, 1, undef, $res->{'barcode'});
227 $res->set_label(label => $opt_label, finished_cb => $steps->{'labeled'});
229 return failure("Not writing label.", $finished_cb);
233 step labeled => sub {
235 return failure($err, $finished_cb) if $err;
237 $res->release(finished_cb => $steps->{'released'});
240 step released => sub {
242 return failure($err, $finished_cb) if $err;
247 main(\&Amanda::MainLoop::quit);
248 Amanda::MainLoop::run();
249 Amanda::Util::finish_application();