Imported Upstream version 3.3.3
[debian/amanda] / perl / Amanda / Taper / Scribe.pm
index 3efbbb59607263ab399824fd669a1ca30477aeb5..6f90d3006e92de3a5f9cba288123c2555aa9f0b5 100644 (file)
@@ -1,8 +1,9 @@
-# Copyright (c) 2009, 2010 Zmanda, Inc.  All Rights Reserved.
+# Copyright (c) 2009-2012 Zmanda, Inc.  All Rights Reserved.
 #
-# This library is free software; you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License version 2.1 as
-# published by the Free Software Foundation.
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+#* License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
 #
 # This library is distributed in the hope that it will be useful, but
 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
@@ -34,6 +35,7 @@ Amanda::Taper::Scribe
   step start_xfer => sub {
     my ($err) = @_;
     my $xfer_dest = $scribe->get_xfer_dest(
+       allow_split => 1,
        max_memory => 64 * 1024,
        can_cache_inform => 0,
        part_size => 150 * 1024**2,
@@ -160,6 +162,7 @@ Call C<get_xfer_dest> to get the transfer element, supplying information on how
 the dump should be split:
 
   $xdest = $scribe->get_xfer_dest(
+       allow_split => $allow_split,
         max_memory => $max_memory,
         # .. splitting parameters
         );
@@ -173,6 +176,10 @@ The splitting parameters to C<get_xfer_dest> are:
 
 =over 4
 
+=item C<allow_split>
+
+this dle is allowed or not to split
+
 =item C<part_size>
 
 the split part size to use, or 0 for no splitting
@@ -213,6 +220,7 @@ should know about.
   use Amanda::Taper::Scribe qw( get_splitting_args_from_config );
   my %splitting_args = get_splitting_args_from_config(
     # Amanda dumptype configuration parameters,
+    dle_allow_split => ..,
     dle_tape_splitsize => ..,
     dle_split_diskbuffer => ..,
     dle_fallback_splitsize => ..,
@@ -435,7 +443,7 @@ sub new {
     my %params = @_;
 
     my $decide_debug = $Amanda::Config::debug_taper || $params{'debug'};
-    for my $rq_param qw(taperscan feedback) {
+    for my $rq_param (qw(taperscan feedback)) {
        croak "required parameter '$rq_param' mising"
            unless exists $params{$rq_param};
     }
@@ -444,6 +452,7 @@ sub new {
        taperscan => $params{'taperscan'},
        feedback => $params{'feedback'},
        debug => $decide_debug,
+       eject_volume => $params{'eject_volume'},
        write_timestamp => undef,
        started => 0,
 
@@ -456,6 +465,7 @@ sub new {
        device => undef,
        device_size => undef,
        device_at_eom => undef, # device still exists, but is full
+       close_volume => undef,
 
         # callback passed to start_dump
        dump_cb => undef,
@@ -463,6 +473,7 @@ sub new {
        # information for the current dumpfile
        dump_header => undef,
        retry_part_on_peom => undef,
+       allow_split => undef,
        xfer => undef,
        xdt => undef,
        xdt_ready => undef,
@@ -483,12 +494,12 @@ sub start {
     my $self = shift;
     my %params = @_;
 
-    for my $rq_param qw(write_timestamp finished_cb) {
+    for my $rq_param (qw(write_timestamp finished_cb)) {
        croak "required parameter '$rq_param' missing"
            unless exists $params{$rq_param};
     }
 
-    die "scribe already started" if $self->{'started'};
+    confess "scribe already started" if $self->{'started'};
 
     $self->dbg("starting");
     $self->{'write_timestamp'} = $params{'write_timestamp'};
@@ -517,7 +528,7 @@ sub quit {
        $self->dbg("quitting");
 
        if ($self->{'xfer'}) {
-           die "Scribe cannot quit while a transfer is active";
+           confess "Scribe cannot quit while a transfer is active";
            # Supporting this would be complicated:
            # - cancel the xfer and wait for it to complete
            # - ensure that the taperscan not be started afterward
@@ -580,7 +591,7 @@ sub check_data_path {
     my $device = $self->get_device();
 
     if (!defined $device) {
-       die "no device is available to check the datapath";
+       confess "no device is available to check the datapath";
     }
 
     my $use_directtcp = $device->directtcp_supported();
@@ -607,16 +618,16 @@ sub get_xfer_dest {
     my $self = shift;
     my %params = @_;
 
-    for my $rq_param qw(max_memory) {
+    for my $rq_param (qw(max_memory)) {
        croak "required parameter '$rq_param' missing"
            unless exists $params{$rq_param};
     }
 
-    die "not yet started"
+    confess "not yet started"
        unless $self->{'write_timestamp'} and $self->{'started'};
-    die "xfer element already returned"
+    confess "xfer element already returned"
        if ($self->{'xdt'});
-    die "xfer already running"
+    confess "xfer already running"
        if ($self->{'xfer'});
 
     $self->{'xfer'} = undef;
@@ -633,6 +644,7 @@ sub get_xfer_dest {
     # set the callback
     $self->{'dump_cb'} = undef;
     $self->{'retry_part_on_peom'} = 1;
+    $self->{'allow_split'} = 0;
     $self->{'start_part_on_xdt_ready'} = 0;
 
     # start getting parameters together to determine what kind of splitting
@@ -641,10 +653,11 @@ sub get_xfer_dest {
     my ($use_mem_cache, $disk_cache_dirname) = (0, undef);
     my $can_cache_inform = $params{'can_cache_inform'};
     my $part_cache_type = $params{'part_cache_type'} || 'none';
+    my $allow_split = $params{'allow_split'};
 
     my $xdt_first_dev = $self->get_device();
     if (!defined $xdt_first_dev) {
-       die "no device is available to create an xfer_dest";
+       confess "no device is available to create an xfer_dest";
     }
     my $leom_supported = $xdt_first_dev->property_get("leom");
     my $use_directtcp = $xdt_first_dev->directtcp_supported();
@@ -688,6 +701,19 @@ sub get_xfer_dest {
        $self->{'retry_part_on_peom'} = 0;
     }
 
+    if ($allow_split &&
+       ($can_cache_inform ||
+        !defined($part_cache_type) ||
+        $part_cache_type eq 'disk' ||
+        $part_cache_type eq 'memory' ||
+        $leom_supported)) {
+       $self->{'allow_split'} = 1;
+    } else {
+       $self->{'allow_split'} = 0;
+    }
+
+    $self->{'retry_part_on_peom'} = 0 if !$self->{'allow_split'};
+
     debug("Amanda::Taper::Scribe preparing to write, part size $part_size, "
        . "$dest_text ($dest_type) "
        . ($leom_supported? " (LEOM supported)" : " (no LEOM)"));
@@ -722,7 +748,7 @@ sub start_dump {
     my $self = shift;
     my %params = @_;
 
-    die "no xfer dest set up; call get_xfer_dest first"
+    confess "no xfer dest set up; call get_xfer_dest first"
         unless defined $self->{'xdt'};
 
     # get the header ready for writing (totalparts was set by the caller)
@@ -742,29 +768,23 @@ sub cancel_dump {
     my $self = shift;
     my %params = @_;
 
-    die "no xfer dest set up; call get_xfer_dest first"
+    confess "no xfer dest set up; call get_xfer_dest first"
        unless defined $self->{'xdt'};
 
     # set up the dump_cb for when this dump is done, and keep the xfer
     $self->{'dump_cb'} = $params{'dump_cb'};
     $self->{'xfer'} = $params{'xfer'};
 
-    # XXX The cancel should call dump_cb, but right now the xfer stays hung in
-    # accept.  So we leave the xfer to its hang, and dump_cb is called and xdt
-    # and xfer are set to undef.  This should be fixed in 3.2.
+    # The cancel will can dump_cb.
 
     $self->{'xfer'}->cancel();
 
-    $self->{'dump_cb'}->(
-       result => "FAILED",
-       device_errors => [],
-       config_denial_message => undef,
-       size => 0,
-       duration => 0.0,
-       total_duration => 0,
-       nparts => 0);
-    $self->{'xdt'} = undef;
-    $self->{'xfer'} = undef;
+}
+
+sub close_volume {
+    my $self = shift;
+
+    $self->{'close_volume'} = 1;
 }
 
 sub get_bytes_written {
@@ -790,6 +810,11 @@ sub _start_part {
        return
     }
 
+    if ($self->{'close_volume'}) {
+       $self->{'close_volume'} = undef;
+       return $self->_get_new_volume();
+    }
+
     # we need an actual, permitted device at this point, so if we don't have
     # one, then defer this start_part call until we do.  The device may still
     # exist, but be at EOM, if the last dump failed at EOM and was not retried
@@ -850,7 +875,7 @@ sub _xmsg_part_done {
        $self->dbg("not notifying for empty, successful part");
     } else {
        # double-check partnum
-       die "Part numbers do not match!"
+       confess "Part numbers do not match! $self->{'dump_header'}->{'partnum'} $msg->{'partnum'}"
            unless ($self->{'dump_header'}->{'partnum'} == $msg->{'partnum'});
 
        # notify
@@ -890,7 +915,7 @@ sub _xmsg_part_done {
            }
 
            # if the part failed..
-           if (!$msg->{'successful'}) {
+           if (!$msg->{'successful'} || !$self->{'allow_split'}) {
                # if no caching was going on, then the dump has failed
                if (!$self->{'retry_part_on_peom'}) {
                    # mark this device as at EOM, since we are not going to look
@@ -901,7 +926,7 @@ sub _xmsg_part_done {
                    if ($self->{'device'}->status() != $DEVICE_STATUS_SUCCESS) {
                        $msg = $self->{'device'}->error_or_status();
                    }
-                   $self->_operation_failed(device_error => $msg);
+                   $self->_operation_failed(device_error => "$msg, splitting not enabled");
                    return;
                }
 
@@ -916,11 +941,12 @@ sub _xmsg_part_done {
            # if the part was unsuccessful, but the xfer dest has reason to believe
            # this is not due to EOM, then the dump is done
            if (!$msg->{'successful'}) {
-               my $msg = "unknown error while dumping";
                if ($self->{'device'}->status() != $DEVICE_STATUS_SUCCESS) {
                    $msg = $self->{'device'}->error_or_status();
+                   $self->_operation_failed(device_error => $msg);
+               } else {
+                   $self->_operation_failed();
                }
-               $self->_operation_failed(device_error => $msg);
                return;
            }
 
@@ -960,6 +986,14 @@ sub _xmsg_done {
     }
 }
 
+sub abort_setup {
+    my $self = shift;
+    my %params = @_;
+
+    $self->{'dump_cb'} = $params{'dump_cb'};
+    $self->_dump_done();
+}
+
 sub _dump_done {
     my $self = shift;
 
@@ -967,13 +1001,19 @@ sub _dump_done {
 
     # determine the correct final status - DONE if we're done, PARTIAL
     # if we've started writing to the volume, otherwise FAILED
-    if (@{$self->{'device_errors'}} or $self->{'config_denial_message'}) {
-       $result = $self->{'started_writing'}? 'PARTIAL' : 'FAILED';
+    if (!$self->{'started_writing'}) {
+       $result = 'FAILED';
+    } elsif (@{$self->{'device_errors'}} or $self->{'config_denial_message'} or
+            !$self->{'last_part_successful'}) {
+       $result = 'PARTIAL';
     } else {
        $result = 'DONE';
     }
 
     my $dump_cb = $self->{'dump_cb'};
+
+    return if !defined $dump_cb;
+
     my %dump_cb_args = (
        result => $result,
        device_errors => $self->{'device_errors'},
@@ -1008,7 +1048,7 @@ sub _operation_failed {
 
     my $error_message = $params{'device_error'}
                     || $params{'config_denial_message'}
-                    || 'no reason';
+                    || 'input error';
     $self->dbg("operation failed: $error_message");
 
     # tuck the message away as desired
@@ -1028,8 +1068,6 @@ sub _operation_failed {
         if (defined $self->{'dump_cb'}) {
             # _dump_done constructs the dump_cb from $self parameters
             $self->_dump_done();
-        } else {
-            die "error with no callback to handle it: $error_message";
         }
     }
 }
@@ -1040,11 +1078,13 @@ sub _release_reservation {
     my $self = shift;
     my %params = @_;
     my @errors;
+    my $do_eject = 0;
 
     my ($label, $fm, $kb);
 
     # if we've already written a volume, log it
     if ($self->{'device'} and defined $self->{'device'}->volume_label) {
+       $do_eject = 1 if $self->{'eject_volume'};
        $label = $self->{'device'}->volume_label();
        $fm = $self->{'device'}->file();
        $kb = $self->{'device_size'} / 1024;
@@ -1065,7 +1105,7 @@ sub _release_reservation {
     $self->{'device'} = undef;
     $self->{'device_at_eom'} = 0;
 
-    $self->{'reservation'}->release(finished_cb => sub {
+    $self->{'reservation'}->release(eject => $do_eject, finished_cb => sub {
        my ($err) = @_;
        push @errors, "$err" if $err;
 
@@ -1073,10 +1113,13 @@ sub _release_reservation {
 
        # notify the feedback that we've finished and released a tape
        if ($label) {
-           $self->{'feedback'}->scribe_notif_tape_done(
+           return $self->{'feedback'}->scribe_notif_tape_done(
                volume_label => $label,
                size => $kb * 1024,
-               num_files => $fm);
+               num_files => $fm,
+               finished_cb => sub {
+                $params{'finished_cb'}->(@errors? join("; ", @errors) : undef);
+               });
        }
 
        $params{'finished_cb'}->(@errors? join("; ", @errors) : undef);
@@ -1133,6 +1176,7 @@ sub _volume_cb  {
        $new_scribe->{'dump_cb'} = $self->{'dump_cb'};
        $new_scribe->{'dump_header'} = $self->{'dump_header'};
        $new_scribe->{'retry_part_on_peom'} = $self->{'retry_part_on_peom'};
+       $new_scribe->{'allow_split'} = $self->{'allow_split'};
        $new_scribe->{'split_method'} = $self->{'split_method'};
        $new_scribe->{'xfer'} = $self->{'xfer'};
        $new_scribe->{'xdt'} = $self->{'xdt'};
@@ -1205,75 +1249,91 @@ sub _volume_cb  {
     # inform the xdt about this new device before starting it
     $self->{'xdt'}->use_device($device);
 
-    my $result = $self->_device_start($device, $access_mode, $new_label, $is_new);
-    if ($result == 0) {
-       # try reading the label to see whether we erased the tape
-       my $erased = 0;
-       CHECK_READ_LABEL: {
+    my $cbX = sub {};
+    my $steps = define_steps
+       cb_ref => \$cbX;
+
+    step device_start => sub {
+       $self->_device_start($reservation, $access_mode, $new_label, $is_new,
+                            $steps->{'device_started'});
+    };
+
+    step device_started => sub {
+       my $result = shift;
+
+       if ($result =~ /\D/) {
+           $self->{'feedback'}->scribe_notif_new_tape(
+               error => $result,
+               volume_label => undef);
+           $self->_get_new_volume();
+           return $cbX->();
+       } elsif ($result == 0) {
+           # try reading the label to see whether we erased the tape
+           my $erased = 0;
+           CHECK_READ_LABEL: {
            # don't worry about erasing new tapes
-           if ($is_new) {
-               last CHECK_READ_LABEL;
-           }
+               if ($is_new) {
+                   last CHECK_READ_LABEL;
+               }
 
-           $device->read_label();
+               $device->finish();
+               $device->read_label();
 
-           # does the device think something is broken now?
-           if (($device->status & ~$DEVICE_STATUS_VOLUME_UNLABELED)
-               and !($device->status & $DEVICE_STATUS_VOLUME_UNLABELED)) {
-               $erased = 1;
-               last CHECK_READ_LABEL;
-           }
+               # does the device think something is broken now?
+               if (($device->status & ~$DEVICE_STATUS_VOLUME_UNLABELED)
+                   and !($device->status & $DEVICE_STATUS_VOLUME_UNLABELED)) {
+                   $erased = 1;
+                   last CHECK_READ_LABEL;
+               }
 
-           # has the label changed?
-           my $vol_label = $device->volume_label;
-           if ((!defined $old_label and defined $vol_label)
-               or (defined $old_label and !defined $vol_label)
-               or (defined $old_label and $old_label ne $vol_label)) {
-               $erased = 1;
-               last CHECK_READ_LABEL;
-           }
+               # has the label changed?
+               my $vol_label = $device->volume_label;
+               if ((!defined $old_label and defined $vol_label)
+                   or (defined $old_label and !defined $vol_label)
+                   or (defined $old_label and $old_label ne $vol_label)) {
+                   $erased = 1;
+                   last CHECK_READ_LABEL;
+               }
 
-           # has the timestamp changed?
-           my $vol_timestamp = $device->volume_time;
-           if ((!defined $old_timestamp and defined $vol_timestamp)
-               or (defined $old_timestamp and !defined $vol_timestamp)
-               or (defined $old_timestamp and $old_timestamp ne $vol_timestamp)) {
-               $erased = 1;
-               last CHECK_READ_LABEL;
+               # has the timestamp changed?
+               my $vol_timestamp = $device->volume_time;
+               if ((!defined $old_timestamp and defined $vol_timestamp)
+                   or (defined $old_timestamp and !defined $vol_timestamp)
+                   or (defined $old_timestamp and $old_timestamp ne $vol_timestamp)) {
+                   $erased = 1;
+                   last CHECK_READ_LABEL;
+               }
            }
+
+           $self->{'feedback'}->scribe_notif_new_tape(
+               error => "while labeling new volume: " . $device->error_or_status(),
+               volume_label => $erased? $new_label : undef);
+
+           $self->_get_new_volume();
+           return $cbX->();
        }
 
-       $self->{'feedback'}->scribe_notif_new_tape(
-           error => "while labeling new volume: " . $device->error_or_status(),
-           volume_label => $erased? $new_label : undef);
+       $new_label = $device->volume_label;
 
-       return $self->_get_new_volume();
-    } elsif ($result != 1) {
+       # success!
        $self->{'feedback'}->scribe_notif_new_tape(
-           error => $result,
-           volume_label => undef);
-       return $self->_get_new_volume();
-    }
+           error => undef,
+           volume_label => $new_label);
 
-    $new_label = $device->volume_label;
-
-    # success!
-    $self->{'feedback'}->scribe_notif_new_tape(
-       error => undef,
-       volume_label => $new_label);
+       $self->{'reservation'}->set_label(label => $new_label,
+           finished_cb => $steps->{'set_labelled'});
+    };
 
-    # notify the changer that we've labeled the tape, and start the part.
-    my $label_set_cb = make_cb(label_set_cb => sub {
+    step set_labelled => sub {
        my ($err) = @_;
        if ($err) {
            $self->{'feedback'}->scribe_notif_log_info(
                message => "Error from set_label: $err");
            # fall through to start_part anyway...
        }
-       return $self->_start_part();
-    });
-    $self->{'reservation'}->set_label(label => $new_label,
-       finished_cb => $label_set_cb);
+       $self->_start_part();
+       return $cbX->();
+    }
 }
 
 # return 0 for device->start error
@@ -1281,52 +1341,89 @@ sub _volume_cb  {
 # return a message for others error
 sub _device_start {
     my $self = shift;
-    my ($device, $access_mode, $new_label, $is_new) = @_;
+    my ($reservation, $access_mode, $new_label, $is_new, $finished_cb) = @_;
 
+    my $device = $reservation->{'device'};
     my $tl = $self->{'taperscan'}->{'tapelist'};
+    my $meta;
 
     if (!defined $tl) { # For Mock::Taperscan in installcheck
        if (!$device->start($access_mode, $new_label, $self->{'write_timestamp'})) {
-           return 0;
+           return $finished_cb->(0);
        } else {
-           return 1;
+           return $finished_cb->(1);
        }
     }
 
-    if ($is_new) {
-       # generate the new label and write it to the tapelist file
-       $tl->reload(1);
-       ($new_label, my $err) = $self->{'taperscan'}->make_new_tape_label();
-       if (!defined $new_label) {
-           $tl->unlock();
-           return $err;
-       } else {
-           $tl->add_tapelabel('0', $new_label, undef, 0);
-           $tl->write();
-       }
-       $self->dbg("generate new label '$new_label'");
-    }
+    my $steps = define_steps
+       cb_ref => \$finished_cb;
+
+    step setup => sub {
+       return $reservation->get_meta_label(
+                               finished_cb => $steps->{'got_meta_label'});
+    };
+
+    step got_meta_label => sub {
+       my ($err, $meta) = @_;
 
-    # write the label to the device
-    if (!$device->start($access_mode, $new_label, $self->{'write_timestamp'})) {
        if ($is_new) {
-           # remove the generated label from the tapelist file
+           # generate the new label and write it to the tapelist file
            $tl->reload(1);
-           $tl->remove_tapelabel($new_label);
+           if (!$meta) {
+               ($meta, $err) = $reservation->make_new_meta_label();
+               if (defined $err) {
+                   $tl->unlock();
+                   return $finished_cb->($err);
+               }
+           }
+           ($new_label, my $err) = $reservation->make_new_tape_label(
+                                       meta => $meta);
+           if (!defined $new_label) {
+               $tl->unlock();
+               return $finished_cb->($err);
+           }
+           $tl->add_tapelabel('0', $new_label, undef, 1, $meta,
+                              $reservation->{'barcode'});
            $tl->write();
-        }
-       return 0;
-    }
+           $self->dbg("generate new label '$new_label'");
+       } else {
+           $tl->reload(0);
+           my $tle = $tl->lookup_tapelabel($new_label);
+           $meta = $tle->{'meta'} if !defined $meta && $tle->{'meta'};
+           my $barcode = $tle->{'barcode'};
+           if (defined $barcode and $barcode ne $reservation->{'barcode'}) {
+               return $finished_cb->("tapelist for label '$new_label' have barcode '$barcode' but changer report '" . $reservation->{'barcode'} . "'");
+           }
+       }
 
-    # rewrite the tapelist file
-    $tl->reload(1);
-    my $tle = $tl->lookup_tapelabel($new_label);
-    $tl->remove_tapelabel($new_label);
-    $tl->add_tapelabel($self->{'write_timestamp'}, $new_label,
-                      $tle? $tle->{'comment'} : undef, 1);
-    $tl->write();
+       # write the label to the device
+       if (!$device->start($access_mode, $new_label, $self->{'write_timestamp'})) {
+           if ($is_new) {
+               # remove the generated label from the tapelist file
+               $tl->reload(1);
+               $tl->remove_tapelabel($new_label);
+               $tl->write();
+            }
+           return $finished_cb->(0);
+       }
 
-    return 1;
+       # rewrite the tapelist file
+       $tl->reload(1);
+       my $tle = $tl->lookup_tapelabel($new_label);
+       $meta = $tle->{'meta'} if !$meta && $tle->{'meta'};
+       $tl->remove_tapelabel($new_label);
+       $tl->add_tapelabel($self->{'write_timestamp'}, $new_label,
+                          $tle? $tle->{'comment'} : undef, 1, $meta,
+                          $reservation->{'barcode'}, $device->block_size/1024);
+       $tl->write();
+
+       $reservation->set_meta_label(meta => $meta,
+                                    finished_cb => $steps->{'set_meta_label'});
+    };
+
+    step set_meta_label => sub {
+       return $finished_cb->(1);
+    }
 }
 
 sub dbg {
@@ -1339,12 +1436,12 @@ sub dbg {
 sub get_splitting_args_from_config {
     my %params = @_;
 
-    use Data::Dumper;
     my %splitting_args;
 
+    $splitting_args{'allow_split'} = 0;
     # if dle_splitting is false, then we don't split - easy.
     if (defined $params{'dle_allow_split'} and !$params{'dle_allow_split'}) {
-       return ();
+       return %splitting_args;
     }
 
     # utility for below
@@ -1355,7 +1452,7 @@ sub get_splitting_args_from_config {
        my $fsusage = Amanda::Util::get_fs_usage($dirname);
        confess "$dirname" if (!$fsusage);
 
-       my $avail = $fsusage->{'blocks'} * $fsusage->{'bavail'};
+       my $avail = $fsusage->{'blocksize'} * $fsusage->{'bavail'};
        if ($avail < $part_size) {
            Amanda::Debug::debug("disk cache has $avail bytes available on $dirname, but " .
                                 "needs $part_size");
@@ -1381,11 +1478,26 @@ sub get_splitting_args_from_config {
            unless defined $params{'part_cache_type'};
     }
 
-    # if any of the dle_* parameters are set, use those to set the part_*
-    # parameters, which are emptied out first.
-    if (defined $params{'dle_tape_splitsize'} or
-       defined $params{'dle_split_diskbuffer'} or
-       defined $params{'dle_fallback_splitsize'}) {
+    if (defined $splitting_args{'data_path'} and
+       $splitting_args{'data_path'} eq "DIRECTTCP") {
+       my $ps = $params{'dle_tape_splitsize'};
+       if (defined $ps and $ps > 0) {
+           $params{'part_cache_max_size'} = undef
+       } else {
+           $ps = $params{'part_size'};
+           my $pcms = $params{'part_cache_max_size'};
+           $ps = $pcms if (!defined $ps or (defined $pcms and $pcms < $ps));
+       }
+       $splitting_args{'allow_split'} = 1 if ((defined $ps and $ps > 0) or
+                                              $params{'leom_supported'});
+       $params{'part_size'} = $ps;
+       $params{'part_cache_type'} = 'none';
+       $params{'part_cache_dir'} = undef;
+    } elsif (defined $params{'dle_tape_splitsize'} or
+            defined $params{'dle_split_diskbuffer'} or
+            defined $params{'dle_fallback_splitsize'}) {
+       # if any of the dle_* parameters are set, use those to set the part_*
+       # parameters, which are emptied out first.
 
        $params{'part_size'} = $params{'dle_tape_splitsize'} || 0;
        $params{'part_cache_type'} = 'none';
@@ -1394,6 +1506,7 @@ sub get_splitting_args_from_config {
 
        # part cache type is memory unless we have a split_diskbuffer that fits the bill
        if ($params{'part_size'}) {
+           $splitting_args{'allow_split'} = 1;
            $params{'part_cache_type'} = 'memory';
            if (defined $params{'dle_split_diskbuffer'}
                    and -d $params{'dle_split_diskbuffer'}) {
@@ -1417,6 +1530,8 @@ sub get_splitting_args_from_config {
        my $ps = $params{'part_size'};
        my $pcms = $params{'part_cache_max_size'};
        $ps = $pcms if (!defined $ps or (defined $pcms and $pcms < $ps));
+       $splitting_args{'allow_split'} = 1 if ((defined $ps and $ps > 0) or
+                                              $params{'leom_supported'});
 
        # fail back from 'disk' to 'none' if the disk isn't set up correctly
        if (defined $params{'part_cache_type'} and
@@ -1467,9 +1582,14 @@ sub request_volume_permission {
 }
 
 sub scribe_notif_new_tape { }
-sub scribe_notif_tape_done { }
 sub scribe_notif_part_done { }
 sub scribe_notif_log_info { }
+sub scribe_notif_tape_done {
+    my $self = shift;
+    my %params = @_;
+
+    $params{'finished_cb'}->();
+}
 
 ##
 ## Device Handling
@@ -1478,6 +1598,7 @@ sub scribe_notif_log_info { }
 package Amanda::Taper::Scribe::DevHandling;
 use Amanda::MainLoop;
 use Carp;
+use Amanda::Debug qw( :logging );
 
 # This class handles scanning for volumes, requesting permission for those
 # volumes (the driver likes to feel like it's in control), and providing those
@@ -1541,7 +1662,7 @@ sub quit {
     my $self = shift;
     my %params = @_;
 
-    for my $rq_param qw(finished_cb) {
+    for my $rq_param (qw(finished_cb)) {
        croak "required parameter '$rq_param' mising"
            unless exists $params{$rq_param};
     }
@@ -1587,7 +1708,7 @@ sub get_volume {
     my $self = shift;
     my (%params) = @_;
 
-    die "already processing a volume request"
+    confess "already processing a volume request"
        if ($self->{'volume_cb'});
 
     $self->{'volume_cb'} = $params{'volume_cb'};
@@ -1624,21 +1745,86 @@ sub _start_scanning {
 
     $self->{'scan_running'} = 1;
 
-    $self->{'taperscan'}->scan(result_cb => sub {
+    my $_user_msg_fn = sub {
+       my %params = @_;
+       if (exists($params{'slot_result'})) {
+           if ($params{'does_not_match_labelstr'}) {
+               $self->{'feedback'}->scribe_notif_log_info(
+                   message => "Slot $params{'slot'} with label $params{'label'} do not match labelstr");
+           } elsif ($params{'not_in_tapelist'}) {
+               $self->{'feedback'}->scribe_notif_log_info(
+                   message => "Slot $params{'slot'} with label $params{'label'} is not in the tapelist");
+           } elsif ($params{'active'}) {
+               $self->{'feedback'}->scribe_notif_log_info(
+                   message => "Slot $params{'slot'} with label $params{'label'} is not reusable");
+           } elsif ($params{'not_autolabel'}) {
+               if ($params{'label'}) {
+                   $self->{'feedback'}->scribe_notif_log_info(
+                       message => "Slot $params{'slot'} with label $params{'label'} is not labelable ");
+               } elsif ($params{'empty'}) {
+                   $self->{'feedback'}->scribe_notif_log_info(
+                       message => "Slot $params{'slot'} is empty, autolabel not set");
+               } elsif ($params{'non_amanda'}) {
+                   $self->{'feedback'}->scribe_notif_log_info(
+                       message => "Slot $params{'slot'} is a non-amanda volume, autolabel not set");
+               } elsif ($params{'volume_error'}) {
+                   $self->{'feedback'}->scribe_notif_log_info(
+                       message => "Slot $params{'slot'} is a volume in error: $params{'err'}, autolabel not set");
+               } elsif ($params{'not_success'}) {
+                   $self->{'feedback'}->scribe_notif_log_info(
+                       message => "Slot $params{'slot'} is a device in error: $params{'err'}, autolabel not set");
+               } elsif ($params{'err'}) {
+                   $self->{'feedback'}->scribe_notif_log_info(
+                       message => "$params{'err'}");
+               } else {
+                   $self->{'feedback'}->scribe_notif_log_info(
+                       message => "Slot $params{'slot'} without label is not labelable ");
+               }
+           } elsif ($params{'empty'}) {
+               $self->{'feedback'}->scribe_notif_log_info(
+                   message => "Slot $params{'slot'} is empty, autolabel disabled");
+           } elsif ($params{'non_amanda'}) {
+               $self->{'feedback'}->scribe_notif_log_info(
+                   message => "Slot $params{'slot'} is a non-amanda volume, autolabel disabled");
+           } elsif ($params{'volume_error'}) {
+               $self->{'feedback'}->scribe_notif_log_info(
+                   message => "Slot $params{'slot'} is a volume in error: $params{'err'}, autolabel disabled");
+           } elsif ($params{'not_success'}) {
+               $self->{'feedback'}->scribe_notif_log_info(
+                   message => "Slot $params{'slot'} is a device in error: $params{'err'}, autolabel disabled");
+           } elsif ($params{'err'}) {
+               $self->{'feedback'}->scribe_notif_log_info(
+                   message => "$params{'err'}");
+           } elsif ($params{'not_labelable'}) {
+               $self->{'feedback'}->scribe_notif_log_info(
+                   message => "Slot $params{'slot'} without label can't be labeled");
+           } elsif (!defined $params{'label'}) {
+               $self->{'feedback'}->scribe_notif_log_info(
+                   message => "Slot $params{'slot'} without label can be labeled");
+           } elsif ($params{'relabeled'}) {
+               $self->{'feedback'}->scribe_notif_log_info(
+                   message => "Slot $params{'slot'} with label $params{'label'} will be relabeled");
+           } else {
+               $self->{'feedback'}->scribe_notif_log_info(
+                   message => "Slot $params{'slot'} with label $params{'label'} is usable");
+           }
+       }
+    };
+
+    $self->{'taperscan'}->scan(
+      user_msg_fn => $_user_msg_fn,
+      result_cb => sub {
        my ($error, $reservation, $volume_label, $access_mode, $is_new) = @_;
 
        $self->{'scan_running'} = 0;
        $self->{'scan_finished'} = 1;
 
-       if ($error) {
-           $self->{'scan_error'} = $error;
-       } else {
-           $self->{'reservation'} = $reservation;
-           $self->{'device'} = $reservation->{'device'};
-           $self->{'volume_label'} = $volume_label;
-           $self->{'access_mode'} = $access_mode;
-           $self->{'is_new'} = $is_new;
-       }
+       $self->{'scan_error'} = $error;
+       $self->{'reservation'} = $reservation;
+       $self->{'device'} = $reservation->{'device'} if $reservation;
+       $self->{'volume_label'} = $volume_label;
+       $self->{'access_mode'} = $access_mode;
+       $self->{'is_new'} = $is_new;
 
        $self->_maybe_callback();
     });
@@ -1668,10 +1854,10 @@ sub _start_request {
            } elsif ($params{'cause'} eq 'error') {
                $self->{'error_denial_message'} = $params{'message'};
            } else {
-               die "bad cause '" . $params{'cause'} . "'";
+               confess "bad cause '" . $params{'cause'} . "'";
            }
        } elsif (!defined $params{'allow'}) {
-           die "no allow or cause defined";
+           confess "no allow or cause defined";
        }
 
        $self->_maybe_callback();