#
# Bacula interface to virtual autoloader using disk storage
#
-# $Id$
+# Written by Kern Sibbald
#
+# Bacula(R) - The Network Backup Solution
+#
+# Copyright (C) 2000-2016 Kern Sibbald
+#
+# The original author of Bacula is Kern Sibbald, with contributions
+# from many others, a complete list can be found in the file AUTHORS.
+#
+# You may use this file and others of this release according to the
+# license defined in the LICENSE file, which includes the Affero General
+# Public License, v3.0 ("AGPLv3") and some additional permissions and
+# terms pursuant to its AGPLv3 Section 7.
+#
+# This notice must be preserved when any source code is
+# conveyed and/or propagated.
+#
+# Bacula(R) is a registered trademark of Kern Sibbald.
# If you set in your Device resource
#
# Changer Command = "path-to-this-script/disk-changer %c %o %S %a %d"
# you will have the following input to this script:
#
# So Bacula will always call with all the following arguments, even though
-# in come cases, not all are used.
+# in come cases, not all are used. Note, the Volume name is not always
+# included.
#
-# disk-changer "changer-device" "command" "slot" "archive-device" "drive-index"
-# $1 $2 $3 $4 $5
+# disk-changer "changer-device" "command" "slot" "archive-device" "drive-index" "volume"
+# $1 $2 $3 $4 $5 $6
#
# By default the autochanger has 10 Volumes and 1 Drive.
#
# any other part of the directory name. These restrictions could be
# easily removed by any clever script jockey.
#
-# Full example: disk-changer /var/bacula/conf load 1 /var/bacula/drive0 0
+# Full example: disk-changer /var/bacula/conf load 1 /var/bacula/drive0 0 TestVol001
#
# The Volumes will be created with names slot1, slot2, slot3, ... maxslot in the
# base directory. In the above example the base directory is /var/bacula.
# /var/bacula/slot3, ...) this script will create a /var/bacula/loadedn
# file to keep track of what Slot is loaded. You should not change this file.
#
+# Modified 8 June 2010 to accept Volume names from the calling program as arg 6.
+# In this case, rather than storing the data in slotn, it is stored in the
+# Volume name. Note: for this to work, Volume names may not include spaces.
#
wd=@working_dir@
#
dbgfile="$wd/disk-changer.log"
debug() {
- if test -e $dbgfile; then
+ if test -f $dbgfile; then
echo "`date +\"%Y%m%d-%H:%M:%S\"` $*" >> $dbgfile
fi
}
#
get_dir() {
bn=`basename $device`
- dir=`echo "$device" | sed -e s%/$bn%%g -`
+ dir=`echo "$device" | sed -e s%/$bn%%g`
if [ ! -d $dir ]; then
- echo "ERROR: Autochanger directory \"$dir\" does not exist.\n"
- echo " You must create it.\n"
+ echo "ERROR: Autochanger directory \"$dir\" does not exist."
+ echo " You must create it."
exit 1
fi
}
+#
+# Get the Volume name from the call line, or directly from
+# the volslotn information.
+#
+get_vol() {
+ havevol=0
+ debug "vol=$volume"
+ if test "x$volume" != x && test "x$volume" != "x*NONE*" ; then
+ debug "touching $dir/$volume"
+ touch $dir/$volume
+ echo "$volume" >$dir/volslot${slot}
+ havevol=1
+ elif [ -f $dir/volslot${slot} ]; then
+ volume=`cat $dir/volslot${slot}`
+ havevol=1
+ fi
+}
+
# Setup arguments
ctl=$1
slot=$3
device=$4
drive=$5
+volume=$6
# set defaults
maxdrive=1
# all others are a minimum of 5
#
case $2 in
- list)
+ list|listall)
check_parm_count $# 2
;;
slots)
check_parm_count $# 2
;;
+ transfer)
+ check_parm_count $# 4
+ if [ $slot -gt $maxslot ]; then
+ echo "Slot ($slot) out of range (1-$maxslot)"
+ debug "Error: Slot ($slot) out of range (1-$maxslot)"
+ exit 1
+ fi
+ ;;
*)
check_parm_count $# 5
if [ $drive -gt $maxdrive ]; then
echo "Drive ($drive) out of range (0-$maxdrive)"
+ debug "Error: Drive ($drive) out of range (0-$maxdrive)"
exit 1
fi
if [ $slot -gt $maxslot ]; then
echo "Slot ($slot) out of range (1-$maxslot)"
+ debug "Error: Slot ($slot) out of range (1-$maxslot)"
exit 1
fi
;;
esac
-
-debug "Parms: $ctl $cmd $slot $device $drive"
+debug "Parms: $ctl $cmd $slot $device $drive $volume $havevol"
case $cmd in
unload)
- debug "Doing disk -f $ctl unload $slot $device $drive"
+ debug "Doing disk -f $ctl unload $slot $device $drive $volume"
get_dir
- echo "0" >$dir/loaded${drive}
- unlink $device 2>/dev/null >/dev/null
- rm -f $device
+ if [ -f $dir/loaded${drive} ]; then
+ ld=`cat $dir/loaded${drive}`
+ else
+ echo "Storage Element $slot is Already Full"
+ debug "Unload error: $dir/loaded${drive} is already unloaded"
+ exit 1
+ fi
+ if [ $slot -eq $ld ]; then
+ echo "0" >$dir/loaded${drive}
+ unlink $device 2>/dev/null >/dev/null
+ unlink ${device}.add 2>/dev/null >/dev/null
+ rm -f ${device} ${device}.add
+ else
+ echo "Storage Element $slot is Already Full"
+ debug "Unload error: $dir/loaded${drive} slot=$ld is already unloaded"
+ exit 1
+ fi
;;
load)
- debug "Doing disk $ctl load $slot $device $drive"
+ debug "Doing disk $ctl load $slot $device $drive $volume"
get_dir
+ i=0
+ # Check if slot already in a drive
+ while [ $i -le $maxdrive ]; do
+ if [ -f $dir/loaded${i} ]; then
+ ld=`cat $dir/loaded${i}`
+ else
+ ld=0
+ fi
+ if [ $ld -eq $slot ]; then
+ echo "Drive ${i} Full (Storage element ${ld} loaded)"
+ debug "Load error: Cannot load Slot=${ld} in drive=$drive. Already in drive=${i}"
+ exit 1
+ fi
+ i=`expr $i + 1`
+ done
+ # Check if we have a Volume name
+ get_vol
+ if [ $havevol -eq 0 ]; then
+ # check if slot exists
+ if [ ! -f $dir/slot${slot} ] ; then
+ echo "source Element Address $slot is Empty"
+ debug "Load error: source Element Address $slot is Empty"
+ exit 1
+ fi
+ fi
+ if [ -f $dir/loaded${drive} ]; then
+ ld=`cat $dir/loaded${drive}`
+ else
+ ld=0
+ fi
+ if [ $ld -ne 0 ]; then
+ echo "Drive ${drive} Full (Storage element ${ld} loaded)"
+ echo "Load error: Drive ${drive} Full (Storage element ${ld} loaded)"
+ exit 1
+ fi
echo "0" >$dir/loaded${drive}
unlink $device 2>/dev/null >/dev/null
- rm -f $device
- ln -s $dir/slot${slot} $device
- rtn=$?
+ unlink ${device}.add 2>/dev/null >/dev/null
+ rm -f ${device} ${device}.add
+ if [ $havevol -ne 0 ]; then
+ ln -s $dir/$volume $device
+ ln -s $dir/${volume}.add ${device}.add
+ rtn=$?
+ else
+ ln -s $dir/slot${slot} $device
+ ln -s $dir/slot${slot}.add ${device}.add
+ rtn=$?
+ fi
if [ $rtn -eq 0 ]; then
echo $slot >$dir/loaded${drive}
fi
else
i=1
while [ $i -le $maxslot ]; do
- echo "$i:"
+ slot=$i
+ volume=
+ get_vol
+ if [ $havevol -eq 0 ]; then
+ echo "$i:"
+ else
+ echo "$i:$volume"
+ fi
i=`expr $i + 1`
done
fi
exit 0
;;
+ listall)
+ # ***FIXME*** must add new Volume stuff
+ make_temp_file
+ debug "Doing disk -f $ctl -- to list volumes"
+ get_dir
+ if [ ! -f $dir/barcodes ]; then
+ exit 0
+ fi
+
+ # we print drive content seen by autochanger
+ # and we also remove loaded media from the barcode list
+ i=0
+ while [ $i -le $maxdrive ]; do
+ if [ -f $dir/loaded${i} ]; then
+ ld=`cat $dir/loaded${i}`
+ v=`awk -F: "/^$ld:/"' { print $2 }' $dir/barcodes`
+ echo "D:$i:F:$ld:$v"
+ echo "^$ld:" >> $TMPFILE
+ fi
+ i=`expr $i + 1`
+ done
+
+ # Empty slots are not in barcodes file
+ # When we detect a gap, we print missing rows as empty
+ # At the end, we fill the gap between the last entry and maxslot
+ grep -v -f $TMPFILE $dir/barcodes | sort -n | \
+ perl -ne 'BEGIN { $cur=1 }
+ if (/(\d+):(.+)?/) {
+ if ($cur == $1) {
+ print "S:$1:F:$2\n"
+ } else {
+ while ($cur < $1) {
+ print "S:$cur:E\n";
+ $cur++;
+ }
+ }
+ $cur++;
+ }
+ END { while ($cur < '"$maxslot"') { print "S:$cur:E\n"; $cur++; } } '
+
+ rm -f $TMPFILE
+ exit 0
+ ;;
+ transfer)
+ # ***FIXME*** must add new Volume stuff
+ get_dir
+ make_temp_file
+ slotdest=$device
+ if [ -f $dir/slot{$slotdest} ]; then
+ echo "destination Element Address $slot is Full"
+ exit 1
+ fi
+ if [ ! -f $dir/slot${slot} ] ; then
+ echo "source Element Address $slot is Empty"
+ exit 1
+ fi
+
+ echo "Transfering $slot to $slotdest"
+ mv $dir/slot${slot} $dir/slot{$slotdest}
+ mv $dir/slot${slot}.add $dir/slot{$slotdest}.add
+
+ if [ -f $dir/barcodes ]; then
+ sed "s/^$slot:/$slotdest:/" > $TMPFILE
+ sort -n $TMPFILE > $dir/barcodes
+ fi
+ exit 0
+ ;;
loaded)
debug "Doing disk -f $ctl $drive -- to find what is loaded"
get_dir
if [ -f $dir/loaded${drive} ]; then
- cat $dir/loaded${drive}
+ a=`cat $dir/loaded${drive}`
else
- echo "0"
+ a="0"
fi
+ debug "Loaded: drive=$drive is $a"
+ echo $a
exit
;;