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@';
25 use Amanda::Util qw( :constants );
26 use Amanda::Config qw( :init :getconf );
27 use Amanda::Logfile qw( :logtype_t log_add $amanda_log_trace_log );
29 use Amanda::Device qw( :constants );
32 use Amanda::Taper::Scan;
33 use Amanda::Interactivity;
36 Amanda::Util::setup_application("amcheck-device", "server", $CONTEXT_CMDLINE);
38 my $config_overrides = new_config_overrides($#ARGV+1);
40 Getopt::Long::Configure(qw{bundling});
42 'version' => \&Amanda::Util::version_opt,
43 'o=s' => sub { add_config_override_opt($config_overrides, $_[1]); },
48 print STDERR "USAGE: amcheck-device <config> [-w] <config-overwrites>";
56 set_config_overrides($config_overrides);
57 config_init($CONFIG_INIT_EXPLICIT_NAME, $ARGV[0]);
58 my ($cfgerr_level, @cfgerr_errors) = config_errors();
59 if ($cfgerr_level >= $CFGERR_WARNINGS) {
60 config_print_errors();
61 if ($cfgerr_level >= $CFGERR_ERRORS) {
62 print STDERR "Errors processing config file";
67 Amanda::Util::finish_setup($RUNNING_AS_DUMPUSER);
72 if (exists $params{'scan_failed'}) {
73 print STDERR "Taper scan algorithm did not find an acceptable volume.\n";
74 if ($params{'expected_label'} or $params{'expected_new'}) {
76 if ($params{'expected_label'}) {
77 push @exp, "volume '$params{expected_label}'";
79 if ($params{'expected_new'}) {
80 push @exp, "a new volume";
82 my $exp = join(" or ", @exp);
83 print STDERR " (expecting $exp)\n";
85 } elsif (exists($params{'text'})) {
86 print STDERR "$params{'text'}\n";
87 } elsif (exists($params{'scan_slot'})) {
88 print STDERR "slot $params{'slot'}:";
89 } elsif (exists($params{'search_label'})) {
90 print STDERR "Searching for label '$params{'label'}':";
91 } elsif (exists($params{'slot_result'}) ||
92 exists($params{'search_result'})) {
93 if (defined($params{'err'})) {
94 if (exists($params{'search_result'}) &&
95 defined($params{'err'}->{'this_slot'})) {
96 print STDERR "slot $params{'err'}->{'this_slot'}:";
98 print STDERR " $params{'err'}\n";
99 } elsif (!$params{'res'}) {
100 my $volume_label = $params{'label'};
101 if ($params{'active'}) {
102 print STDERR " volume '$volume_label' is still active and cannot be overwritten\n";
103 } elsif ($params{'does_not_match_labelstr'}) {
104 print STDERR " volume '$volume_label' does not match labelstr '$params{'labelstr'}'\n";
105 } elsif ($params{'not_in_tapelist'}) {
106 print STDERR " volume '$volume_label' is not in the tapelist\n"
108 print STDERR " volume '$volume_label'\n";
110 } else { # res must be defined
111 my $res = $params{'res'};
112 my $dev = $res->{'device'};
113 if (exists($params{'search_result'})) {
114 print STDERR "found in slot $res->{'this_slot'}:";
116 if ($dev->status == $DEVICE_STATUS_SUCCESS) {
117 my $volume_label = $res->{device}->volume_label;
118 if ($params{'active'}) {
119 print STDERR " volume '$volume_label' is still active and cannot be overwritten\n";
120 } elsif ($params{'does_not_match_labelstr'}) {
121 print STDERR " volume '$volume_label' does not match labelstr '$params{'labelstr'}'\n";
122 } elsif ($params{'not_in_tapelist'}) {
123 print STDERR " volume '$volume_label' is not in the tapelist\n"
125 print STDERR " volume '$volume_label'\n";
127 } elsif ($dev->status & $DEVICE_STATUS_VOLUME_UNLABELED and
128 $dev->volume_header and
129 $dev->volume_header->{'type'} == $Amanda::Header::F_EMPTY) {
130 print STDERR " contains an empty volume\n";
131 } elsif ($dev->status & $DEVICE_STATUS_VOLUME_UNLABELED and
132 $dev->volume_header and
133 $dev->volume_header->{'type'} == $Amanda::Header::F_WEIRD) {
134 my $autolabel = getconf($CNF_AUTOLABEL);
135 if ($autolabel->{'non_amanda'}) {
136 print STDERR " contains a non-Amanda volume\n";
138 print STDERR " contains a non-Amanda volume; check and relabel it with 'amlabel -f'\n";
140 } elsif ($dev->status & $DEVICE_STATUS_VOLUME_ERROR) {
141 my $message = $dev->error_or_status();
142 print STDERR " Can't read label: $message\n";
144 my $errmsg = $res->{device}->error_or_status();
145 print STDERR " $errmsg\n";
149 print STDERR "UNKNOWN\n";
154 my ($msg, $finished_cb) = @_;
155 print STDERR "ERROR: $msg\n";
161 my ($finished_cb) = @_;
162 my ($res, $label, $mode);
163 my $tlf = Amanda::Config::config_dir_relative(getconf($CNF_TAPELIST));
164 my $tl = Amanda::Tapelist->new($tlf);
165 my $chg = Amanda::Changer->new(undef, tapelist => $tl);
166 return failure($chg, $finished_cb) if ($chg->isa("Amanda::Changer::Error"));
167 my $interactivity = Amanda::Interactivity->new(
168 name => getconf($CNF_INTERACTIVITY));
169 my $scan_name = getconf($CNF_TAPERSCAN);
170 my $taperscan = Amanda::Taper::Scan->new(algorithm => $scan_name,
172 interactivity => $interactivity,
175 my $steps = define_steps
176 cb_ref => \$finished_cb,
177 finalize => sub { $taperscan->quit(); };
181 result_cb => $steps->{'result_cb'},
182 user_msg_fn => \&_user_msg_fn
186 step result_cb => sub {
187 (my $err, $res, $label, $mode) = @_;
190 $res->release(finished_cb => sub {
191 return failure($err, $finished_cb);
195 return failure($err, $finished_cb);
199 my $modestr = ($mode == $ACCESS_APPEND)? "append" : "write";
200 my $slot = $res->{'this_slot'};
201 if (defined $res->{'device'} and defined $res->{'device'}->volume_label()) {
202 print "Will $modestr to volume '$label' in slot $slot.\n";
204 my $header = $res->{'device'}->volume_header();
205 if (defined $header and defined $header->{'type'} and
206 $header->{'type'} == $Amanda::Header::F_WEIRD) {
207 print "Will $modestr label '$label' to non-Amanda volume in slot $slot.\n";
209 print "Will $modestr label '$label' to new volume in slot $slot.\n";
213 $steps->{'check_access_type'}->();
216 step check_access_type => sub {
217 my $mat = $res->{'device'}->property_get('medium_access_type');
218 if (defined $mat and $mat == $MEDIA_ACCESS_MODE_WRITE_ONLY) {
219 print "WARNING: Media access mode is WRITE_ONLY; dumps may not be recoverable\n";
222 if (getconf_seen($CNF_DEVICE_OUTPUT_BUFFER_SIZE)) {
223 my $dobs = getconf($CNF_DEVICE_OUTPUT_BUFFER_SIZE);
224 my $block_size = $res->{'device'}->property_get("BLOCK_SIZE");
225 if ($block_size * 2 > $dobs) {
226 print "WARNING: DEVICE-OUTPUT-BUFFER-SIZE is not at least twice the block size of the device, it should be increased for better throughput\n";
229 $steps->{'check_overwrite'}->();
232 step check_overwrite => sub {
233 # if we're not overwriting, just release the reservation
235 print "NOTE: skipping tape-writable test\n";
236 return $steps->{'release'}->();
239 if ($mode != $ACCESS_WRITE) {
240 my $modestr = Amanda::Device::DeviceAccessMode_to_string($mode);
241 print "NOTE: taperscan specified access mode $modestr; skipping volume-writeable test\n";
242 return $steps->{'release'}->();
245 print "Writing label '$label' to check writablility\n";
246 if (!$res->{'device'}->start($ACCESS_WRITE, $label, "X")) {
247 print "ERROR: writing to volume: " . $res->{'device'}->error_or_status(), "\n";
250 print "Volume '$label' is writeable.\n";
253 $steps->{'release'}->();
256 step release => sub {
257 $res->release(finished_cb => $steps->{'released'});
260 step released => sub {
262 return failure($err, $finished_cb) if $err;
268 Amanda::MainLoop::call_later(\&do_check, \&Amanda::MainLoop::quit);
269 Amanda::MainLoop::run();
270 Amanda::Util::finish_application();