3 # Bacula interface to virtual autoloader using disk storage
5 # Written by Kern Sibbald
7 # Bacula(R) - The Network Backup Solution
9 # Copyright (C) 2000-2015 Kern Sibbald
10 # Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
12 # The original author of Bacula is Kern Sibbald, with contributions
13 # from many others, a complete list can be found in the file AUTHORS.
15 # You may use this file and others of this release according to the
16 # license defined in the LICENSE file, which includes the Affero General
17 # Public License, v3.0 ("AGPLv3") and some additional permissions and
18 # terms pursuant to its AGPLv3 Section 7.
20 # This notice must be preserved when any source code is
21 # conveyed and/or propagated.
23 # Bacula(R) is a registered trademark of Kern Sibbald.
24 # If you set in your Device resource
26 # Changer Command = "path-to-this-script/disk-changer %c %o %S %a %d"
27 # you will have the following input to this script:
29 # So Bacula will always call with all the following arguments, even though
30 # in come cases, not all are used. Note, the Volume name is not always
33 # disk-changer "changer-device" "command" "slot" "archive-device" "drive-index" "volume"
36 # By default the autochanger has 10 Volumes and 1 Drive.
38 # Note: For this script to work, you *must" specify
40 # in each of the Devices associated with your AutoChanger resource.
42 # changer-device is the name of a file that overrides the default
43 # volumes and drives. It may have:
44 # maxslot=n where n is one based (default 10)
45 # maxdrive=m where m is zero based (default 1 -- i.e. 2 drives)
47 # This code can also simulate barcodes. You simply put
48 # a list of the slots and barcodes in the "base" directory/barcodes.
49 # See below for the base directory definition. Example of a
51 # /var/bacula/barcodes
56 # archive-device is the name of the base directory where you want the
57 # Volumes stored appended with /drive0 for the first drive; /drive1
58 # for the second drive, ... For example, you might use
59 # /var/bacula/drive0 Note: you must not have a trailing slash, and
60 # the string (e.g. /drive0) must be unique, and it must not match
61 # any other part of the directory name. These restrictions could be
62 # easily removed by any clever script jockey.
64 # Full example: disk-changer /var/bacula/conf load 1 /var/bacula/drive0 0 TestVol001
66 # The Volumes will be created with names slot1, slot2, slot3, ... maxslot in the
67 # base directory. In the above example the base directory is /var/bacula.
68 # However, as with tapes, their Bacula Volume names will be stored inside the
69 # Volume label. In addition to the Volumes (e.g. /var/bacula/slot1,
70 # /var/bacula/slot3, ...) this script will create a /var/bacula/loadedn
71 # file to keep track of what Slot is loaded. You should not change this file.
73 # Modified 8 June 2010 to accept Volume names from the calling program as arg 6.
74 # In this case, rather than storing the data in slotn, it is stored in the
75 # Volume name. Note: for this to work, Volume names may not include spaces.
83 # to turn on logging, uncomment the following line
84 #touch $wd/disk-changer.log
86 dbgfile="$wd/disk-changer.log"
88 if test -f $dbgfile; then
89 echo "`date +\"%Y%m%d-%H:%M:%S\"` $*" >> $dbgfile
95 # Create a temporary file
98 TMPFILE=`mktemp -t mtx.XXXXXXXXXX`
99 if test x${TMPFILE} = x; then
100 TMPFILE="$wd/disk-changer.$$"
101 if test -f ${TMPFILE}; then
102 echo "Temp file security problem on: ${TMPFILE}"
108 # check parameter count on commandline
113 if test $pCount -lt $pCountNeed; then
114 echo "usage: disk-changer ctl-device command [slot archive-device drive-index]"
115 echo " Insufficient number of arguments arguments given."
116 if test $pCount -lt 2; then
117 echo " Mimimum usage is first two arguments ..."
119 echo " Command expected $pCountNeed arguments"
126 # Strip off the final name in order to get the Directory ($dir)
127 # that we are dealing with.
130 bn=`basename $device`
131 dir=`echo "$device" | sed -e s%/$bn%%g`
132 if [ ! -d $dir ]; then
133 echo "ERROR: Autochanger directory \"$dir\" does not exist."
134 echo " You must create it."
140 # Get the Volume name from the call line, or directly from
141 # the volslotn information.
146 if test "x$volume" != x && test "x$volume" != "x*NONE*" ; then
147 debug "touching $dir/$volume"
149 echo "$volume" >$dir/volslot${slot}
151 elif [ -f $dir/volslot${slot} ]; then
152 volume=`cat $dir/volslot${slot}`
176 # Check for special cases where only 2 arguments are needed,
177 # all others are a minimum of 5
181 check_parm_count $# 2
184 check_parm_count $# 2
187 check_parm_count $# 4
188 if [ $slot -gt $maxslot ]; then
189 echo "Slot ($slot) out of range (1-$maxslot)"
190 debug "Error: Slot ($slot) out of range (1-$maxslot)"
195 check_parm_count $# 5
196 if [ $drive -gt $maxdrive ]; then
197 echo "Drive ($drive) out of range (0-$maxdrive)"
198 debug "Error: Drive ($drive) out of range (0-$maxdrive)"
201 if [ $slot -gt $maxslot ]; then
202 echo "Slot ($slot) out of range (1-$maxslot)"
203 debug "Error: Slot ($slot) out of range (1-$maxslot)"
210 debug "Parms: $ctl $cmd $slot $device $drive $volume $havevol"
214 debug "Doing disk -f $ctl unload $slot $device $drive $volume"
216 if [ -f $dir/loaded${drive} ]; then
217 ld=`cat $dir/loaded${drive}`
219 echo "Storage Element $slot is Already Full"
220 debug "Unload error: $dir/loaded${drive} is already unloaded"
223 if [ $slot -eq $ld ]; then
224 echo "0" >$dir/loaded${drive}
225 unlink $device 2>/dev/null >/dev/null
226 unlink ${device}.add 2>/dev/null >/dev/null
227 rm -f ${device} ${device}.add
229 echo "Storage Element $slot is Already Full"
230 debug "Unload error: $dir/loaded${drive} slot=$ld is already unloaded"
236 debug "Doing disk $ctl load $slot $device $drive $volume"
239 # Check if slot already in a drive
240 while [ $i -le $maxdrive ]; do
241 if [ -f $dir/loaded${i} ]; then
242 ld=`cat $dir/loaded${i}`
246 if [ $ld -eq $slot ]; then
247 echo "Drive ${i} Full (Storage element ${ld} loaded)"
248 debug "Load error: Cannot load Slot=${ld} in drive=$drive. Already in drive=${i}"
253 # Check if we have a Volume name
255 if [ $havevol -eq 0 ]; then
256 # check if slot exists
257 if [ ! -f $dir/slot${slot} ] ; then
258 echo "source Element Address $slot is Empty"
259 debug "Load error: source Element Address $slot is Empty"
263 if [ -f $dir/loaded${drive} ]; then
264 ld=`cat $dir/loaded${drive}`
268 if [ $ld -ne 0 ]; then
269 echo "Drive ${drive} Full (Storage element ${ld} loaded)"
270 echo "Load error: Drive ${drive} Full (Storage element ${ld} loaded)"
273 echo "0" >$dir/loaded${drive}
274 unlink $device 2>/dev/null >/dev/null
275 unlink ${device}.add 2>/dev/null >/dev/null
276 rm -f ${device} ${device}.add
277 if [ $havevol -ne 0 ]; then
278 ln -s $dir/$volume $device
279 ln -s $dir/${volume}.add ${device}.add
282 ln -s $dir/slot${slot} $device
283 ln -s $dir/slot${slot}.add ${device}.add
286 if [ $rtn -eq 0 ]; then
287 echo $slot >$dir/loaded${drive}
293 debug "Doing disk -f $ctl -- to list volumes"
295 if [ -f $dir/barcodes ]; then
299 while [ $i -le $maxslot ]; do
303 if [ $havevol -eq 0 ]; then
315 # ***FIXME*** must add new Volume stuff
317 debug "Doing disk -f $ctl -- to list volumes"
319 if [ ! -f $dir/barcodes ]; then
323 # we print drive content seen by autochanger
324 # and we also remove loaded media from the barcode list
326 while [ $i -le $maxdrive ]; do
327 if [ -f $dir/loaded${i} ]; then
328 ld=`cat $dir/loaded${i}`
329 v=`awk -F: "/^$ld:/"' { print $2 }' $dir/barcodes`
331 echo "^$ld:" >> $TMPFILE
336 # Empty slots are not in barcodes file
337 # When we detect a gap, we print missing rows as empty
338 # At the end, we fill the gap between the last entry and maxslot
339 grep -v -f $TMPFILE $dir/barcodes | sort -n | \
340 perl -ne 'BEGIN { $cur=1 }
352 END { while ($cur < '"$maxslot"') { print "S:$cur:E\n"; $cur++; } } '
358 # ***FIXME*** must add new Volume stuff
362 if [ -f $dir/slot{$slotdest} ]; then
363 echo "destination Element Address $slot is Full"
366 if [ ! -f $dir/slot${slot} ] ; then
367 echo "source Element Address $slot is Empty"
371 echo "Transfering $slot to $slotdest"
372 mv $dir/slot${slot} $dir/slot{$slotdest}
373 mv $dir/slot${slot}.add $dir/slot{$slotdest}.add
375 if [ -f $dir/barcodes ]; then
376 sed "s/^$slot:/$slotdest:/" > $TMPFILE
377 sort -n $TMPFILE > $dir/barcodes
382 debug "Doing disk -f $ctl $drive -- to find what is loaded"
384 if [ -f $dir/loaded${drive} ]; then
385 a=`cat $dir/loaded${drive}`
389 debug "Loaded: drive=$drive is $a"
395 debug "Doing disk -f $ctl -- to get count of slots"