]> git.sur5r.net Git - bacula/bacula/blob - bacula/scripts/disk-changer.in
9627badfad9d5415c100e85cedb6031ec16d0c93
[bacula/bacula] / bacula / scripts / disk-changer.in
1 #!/bin/sh
2 #
3 # Bacula interface to virtual autoloader using disk storage
4 #
5 #  Written by Kern Sibbald
6 #
7 #   Bacula(R) - The Network Backup Solution
8 #
9 #   Copyright (C) 2000-2015 Kern Sibbald
10 #   Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
11 #
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.
14 #
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.
19 #
20 #   This notice must be preserved when any source code is 
21 #   conveyed and/or propagated.
22 #
23 #   Bacula(R) is a registered trademark of Kern Sibbald.
24 #  If you set in your Device resource
25 #
26 #  Changer Command = "path-to-this-script/disk-changer %c %o %S %a %d"
27 #    you will have the following input to this script:
28 #
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
31 #    included.
32 #
33 #  disk-changer "changer-device" "command" "slot" "archive-device" "drive-index" "volume"
34 #                   $1              $2       $3        $4               $5         $6
35 #
36 # By default the autochanger has 10 Volumes and 1 Drive.
37 #
38 # Note: For this script to work, you *must" specify
39 #    Device Type = File 
40 # in each of the Devices associated with your AutoChanger resource.
41 #
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)
46 #  
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 
50 #   barcodes file:
51 #      /var/bacula/barcodes
52 #      1:Vol001
53 #      2:Vol002
54 #      ...
55
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.
63 #
64 #  Full example: disk-changer /var/bacula/conf load 1 /var/bacula/drive0 0 TestVol001
65 #
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.
72 #
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.
76 #
77
78 wd=@working_dir@
79
80 #
81 # log whats done
82 #
83 # to turn on logging, uncomment the following line
84 #touch $wd/disk-changer.log
85 #
86 dbgfile="$wd/disk-changer.log"
87 debug() {
88     if test -f $dbgfile; then
89         echo "`date +\"%Y%m%d-%H:%M:%S\"` $*" >> $dbgfile
90     fi
91 }
92
93
94 #
95 # Create a temporary file
96 #
97 make_temp_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}"
103         exit 1
104      fi
105   fi
106 }
107
108 # check parameter count on commandline
109 #
110 check_parm_count() {
111     pCount=$1
112     pCountNeed=$2
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 ..."
118         else
119             echo "  Command expected $pCountNeed arguments"
120         fi
121         exit 1
122     fi
123 }
124
125 #
126 # Strip off the final name in order to get the Directory ($dir)
127 #  that we are dealing with.
128 #
129 get_dir() {
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."
135       exit 1
136    fi
137 }
138
139 #
140 # Get the Volume name from the call line, or directly from
141 #  the volslotn information.
142 #
143 get_vol() {
144    havevol=0
145    debug "vol=$volume"
146    if test "x$volume" != x && test "x$volume" != "x*NONE*" ; then
147       debug "touching $dir/$volume"
148       touch $dir/$volume
149       echo "$volume" >$dir/volslot${slot}
150       havevol=1
151    elif [ -f $dir/volslot${slot} ]; then
152       volume=`cat $dir/volslot${slot}`
153       havevol=1
154    fi
155 }
156
157
158 # Setup arguments
159 ctl=$1
160 cmd="$2"
161 slot=$3
162 device=$4
163 drive=$5
164 volume=$6
165
166 # set defaults
167 maxdrive=1
168 maxslot=10
169
170 # Pull in conf file
171 if [ -f $ctl ]; then 
172    . $ctl
173 fi
174
175
176 # Check for special cases where only 2 arguments are needed, 
177 #  all others are a minimum of 5
178 #
179 case $2 in
180     list|listall)
181         check_parm_count $# 2
182         ;;
183     slots)
184         check_parm_count $# 2
185         ;;
186     transfer)
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)"
191            exit 1
192         fi
193         ;;
194     *)
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)"
199            exit 1
200         fi
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)"
204            exit 1
205         fi
206         ;;
207 esac
208
209
210 debug "Parms: $ctl $cmd $slot $device $drive $volume $havevol"
211
212 case $cmd in 
213    unload)
214       debug "Doing disk -f $ctl unload $slot $device $drive $volume"
215       get_dir
216       if [ -f $dir/loaded${drive} ]; then
217          ld=`cat $dir/loaded${drive}`
218       else 
219          echo "Storage Element $slot is Already Full"
220          debug "Unload error: $dir/loaded${drive} is already unloaded"
221          exit 1
222       fi
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
228       else
229          echo "Storage Element $slot is Already Full"
230          debug "Unload error: $dir/loaded${drive} slot=$ld is already unloaded"
231          exit 1
232       fi
233       ;;
234
235    load)
236       debug "Doing disk $ctl load $slot $device $drive $volume"
237       get_dir
238       i=0
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}`
243          else    
244             ld=0
245          fi
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}"
249             exit 1
250          fi
251          i=`expr $i + 1`
252       done
253       # Check if we have a Volume name
254       get_vol
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"
260             exit 1
261          fi
262       fi
263       if [ -f $dir/loaded${drive} ]; then
264          ld=`cat $dir/loaded${drive}`
265       else
266          ld=0
267       fi
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)"
271          exit 1
272       fi
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
280          rtn=$?
281       else
282          ln -s $dir/slot${slot} $device
283          ln -s $dir/slot${slot}.add ${device}.add
284          rtn=$?
285       fi
286       if [ $rtn -eq 0 ]; then
287          echo $slot >$dir/loaded${drive}
288       fi
289       exit $rtn
290       ;;
291
292    list) 
293       debug "Doing disk -f $ctl -- to list volumes"
294       get_dir 
295       if [ -f $dir/barcodes ]; then
296          cat $dir/barcodes
297       else
298          i=1
299          while [ $i -le $maxslot ]; do
300             slot=$i
301             volume=
302             get_vol
303             if [ $havevol -eq 0 ]; then
304                echo "$i:"
305             else
306                echo "$i:$volume"
307             fi
308             i=`expr $i + 1`
309          done
310       fi
311       exit 0
312       ;;
313
314    listall) 
315       # ***FIXME*** must add new Volume stuff
316       make_temp_file
317       debug "Doing disk -f $ctl -- to list volumes"
318       get_dir 
319       if [ ! -f $dir/barcodes ]; then
320           exit 0
321       fi
322
323       # we print drive content seen by autochanger
324       # and we also remove loaded media from the barcode list
325       i=0
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`
330              echo "D:$i:F:$ld:$v"
331              echo "^$ld:" >> $TMPFILE
332          fi
333          i=`expr $i + 1`
334       done
335
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 } 
341        if (/(\d+):(.+)?/) {
342          if ($cur == $1) { 
343            print "S:$1:F:$2\n" 
344          } else { 
345            while ($cur < $1) {
346               print "S:$cur:E\n";
347               $cur++;
348            }
349          }
350          $cur++;
351        } 
352        END { while ($cur < '"$maxslot"') { print "S:$cur:E\n"; $cur++; } } '
353
354       rm -f $TMPFILE
355       exit 0
356       ;;
357    transfer)
358       #  ***FIXME*** must add new Volume stuff
359       get_dir
360       make_temp_file
361       slotdest=$device
362       if [ -f $dir/slot{$slotdest} ]; then
363          echo "destination Element Address $slot is Full"
364          exit 1
365       fi
366       if [ ! -f $dir/slot${slot} ] ; then
367          echo "source Element Address $slot is Empty"
368          exit 1
369       fi
370
371       echo "Transfering $slot to $slotdest"
372       mv $dir/slot${slot} $dir/slot{$slotdest}
373       mv $dir/slot${slot}.add $dir/slot{$slotdest}.add
374
375       if [ -f $dir/barcodes ]; then
376          sed "s/^$slot:/$slotdest:/" >  $TMPFILE
377          sort -n $TMPFILE > $dir/barcodes
378       fi
379       exit 0
380       ;;
381    loaded)
382       debug "Doing disk -f $ctl $drive -- to find what is loaded"
383       get_dir
384       if [ -f $dir/loaded${drive} ]; then
385          a=`cat $dir/loaded${drive}`
386       else
387          a="0"
388       fi
389       debug "Loaded: drive=$drive is $a"
390       echo $a
391       exit
392       ;;
393
394    slots)
395       debug "Doing disk -f $ctl -- to get count of slots"
396       echo $maxslot
397       ;;
398 esac