| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390 |
- #!/bin/sh
- #
- # Bacula interface to virtual autoloader using disk storage
- #
- # Written by Kern Sibbald
- #
- # Copyright (C) 2000-2010 Free Software Foundation Europe e.V.
- #
- # The main author of Bacula is Kern Sibbald, with contributions from
- # many others, a complete list can be found in the file AUTHORS.
- # This program is Free Software; you can redistribute it and/or
- # modify it under the terms of version three of the GNU Affero General Public
- # License as published by the Free Software Foundation, which is
- # listed in the file LICENSE.
- #
- # This program is distributed in the hope that it will be useful, but
- # WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- # General Public License for more details.
- #
- # You should have received a copy of the GNU Affero General Public License
- # along with this program; if not, write to the Free Software
- # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- # 02110-1301, USA.
- #
- # Bacula® is a registered trademark of Kern Sibbald.
- # The licensor of Bacula is the Free Software Foundation Europe
- # (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
- # Switzerland, email:ftf@fsfeurope.org.
- #
- #
- # 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. Note, the Volume name is not always
- # included.
- #
- # 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.
- #
- # Note: For this script to work, you *must" specify
- # Device Type = File
- # in each of the Devices associated with your AutoChanger resource.
- #
- # changer-device is the name of a file that overrides the default
- # volumes and drives. It may have:
- # maxslot=n where n is one based (default 10)
- # maxdrive=m where m is zero based (default 1 -- i.e. 2 drives)
- #
- # This code can also simulate barcodes. You simply put
- # a list of the slots and barcodes in the "base" directory/barcodes.
- # See below for the base directory definition. Example of a
- # barcodes file:
- # /var/bacula/barcodes
- # 1:Vol001
- # 2:Vol002
- # ...
- #
- # archive-device is the name of the base directory where you want the
- # Volumes stored appended with /drive0 for the first drive; /drive1
- # for the second drive, ... For example, you might use
- # /var/bacula/drive0 Note: you must not have a trailing slash, and
- # the string (e.g. /drive0) must be unique, and it must not match
- # 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 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.
- # However, as with tapes, their Bacula Volume names will be stored inside the
- # Volume label. In addition to the Volumes (e.g. /var/bacula/slot1,
- # /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=/var/lib/bacula
- #
- # log whats done
- #
- # to turn on logging, uncomment the following line
- #touch $wd/disk-changer.log
- #
- dbgfile="$wd/disk-changer.log"
- debug() {
- if test -f $dbgfile; then
- echo "`date +\"%Y%m%d-%H:%M:%S\"` $*" >> $dbgfile
- fi
- }
- #
- # Create a temporary file
- #
- make_temp_file() {
- TMPFILE=`mktemp -t mtx.XXXXXXXXXX`
- if test x${TMPFILE} = x; then
- TMPFILE="$wd/disk-changer.$$"
- if test -f ${TMPFILE}; then
- echo "Temp file security problem on: ${TMPFILE}"
- exit 1
- fi
- fi
- }
- # check parameter count on commandline
- #
- check_parm_count() {
- pCount=$1
- pCountNeed=$2
- if test $pCount -lt $pCountNeed; then
- echo "usage: disk-changer ctl-device command [slot archive-device drive-index]"
- echo " Insufficient number of arguments arguments given."
- if test $pCount -lt 2; then
- echo " Mimimum usage is first two arguments ..."
- else
- echo " Command expected $pCountNeed arguments"
- fi
- exit 1
- fi
- }
- #
- # Strip off the final name in order to get the Directory ($dir)
- # that we are dealing with.
- #
- get_dir() {
- bn=`basename $device`
- dir=`echo "$device" | sed -e s%/$bn%%g`
- if [ ! -d $dir ]; then
- 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
- cmd="$2"
- slot=$3
- device=$4
- drive=$5
- volume=$6
- # set defaults
- maxdrive=1
- maxslot=10
- # Pull in conf file
- if [ -f $ctl ]; then
- . $ctl
- fi
- # Check for special cases where only 2 arguments are needed,
- # all others are a minimum of 5
- #
- case $2 in
- 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)"
- exit 1
- fi
- ;;
- *)
- check_parm_count $# 5
- if [ $drive -gt $maxdrive ]; then
- echo "Drive ($drive) out of range (0-$maxdrive)"
- exit 1
- fi
- if [ $slot -gt $maxslot ]; then
- echo "Slot ($slot) out of range (1-$maxslot)"
- exit 1
- fi
- ;;
- esac
- debug "Parms: $ctl $cmd $slot $device $drive $volume $havevol"
- case $cmd in
- unload)
- debug "Doing disk -f $ctl unload $slot $device $drive $volume"
- get_dir
- if [ -f $dir/loaded${drive} ]; then
- ld=`cat $dir/loaded${drive}`
- else
- echo "Storage Element $slot is Already Full"
- exit 1
- fi
- if [ $slot -eq $ld ]; then
- echo "0" >$dir/loaded${drive}
- unlink $device 2>/dev/null >/dev/null
- rm -f $device
- else
- echo "Storage Element $slot is Already Full"
- exit 1
- fi
- ;;
- load)
- debug "Doing disk $ctl load $slot $device $drive $volume"
- get_dir
- i=0
- 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)"
- 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"
- 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)"
- exit 1
- fi
- echo "0" >$dir/loaded${drive}
- unlink $device 2>/dev/null >/dev/null
- rm -f $device
- if [ $havevol -ne 0 ]; then
- ln -s $dir/$volume $device
- rtn=$?
- else
- ln -s $dir/slot${slot} $device
- rtn=$?
- fi
- if [ $rtn -eq 0 ]; then
- echo $slot >$dir/loaded${drive}
- fi
- exit $rtn
- ;;
- list)
- debug "Doing disk -f $ctl -- to list volumes"
- get_dir
- if [ -f $dir/barcodes ]; then
- cat $dir/barcodes
- else
- i=1
- while [ $i -le $maxslot ]; do
- 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}
- 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}
- else
- echo "0"
- fi
- exit
- ;;
- slots)
- debug "Doing disk -f $ctl -- to get count of slots"
- echo $maxslot
- ;;
- esac
|