]> git.sur5r.net Git - bacula/bacula/blob - bacula/scripts/disk-changer.in
License cleanups
[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 # Copyright (C) 2000-2015, Kern Sibbald
8 # License: BSD 2-Clause
9 #
10 #  Bacula® is a registered trademark of Kern Sibbald.
11 #
12 #
13 #  If you set in your Device resource
14 #
15 #  Changer Command = "path-to-this-script/disk-changer %c %o %S %a %d"
16 #    you will have the following input to this script:
17 #
18 #  So Bacula will always call with all the following arguments, even though
19 #    in come cases, not all are used. Note, the Volume name is not always
20 #    included.
21 #
22 #  disk-changer "changer-device" "command" "slot" "archive-device" "drive-index" "volume"
23 #                   $1              $2       $3        $4               $5         $6
24 #
25 # By default the autochanger has 10 Volumes and 1 Drive.
26 #
27 # Note: For this script to work, you *must" specify
28 #    Device Type = File
29 # in each of the Devices associated with your AutoChanger resource.
30 #
31 # changer-device is the name of a file that overrides the default
32 #   volumes and drives.  It may have:
33 #        maxslot=n   where n is one based (default 10)
34 #        maxdrive=m  where m is zero based (default 1 -- i.e. 2 drives)
35 #
36 #   This code can also simulate barcodes. You simply put
37 #   a list of the slots and barcodes in the "base" directory/barcodes.
38 #   See below for the base directory definition.  Example of a
39 #   barcodes file:
40 #      /var/bacula/barcodes
41 #      1:Vol001
42 #      2:Vol002
43 #      ...
44 #
45 # archive-device is the name of the base directory where you want the
46 #  Volumes stored appended with /drive0 for the first drive; /drive1
47 #  for the second drive, ... For example, you might use
48 #  /var/bacula/drive0  Note: you must not have a trailing slash, and
49 #  the string (e.g. /drive0) must be unique, and it must not match
50 #  any other part of the directory name. These restrictions could be
51 #  easily removed by any clever script jockey.
52 #
53 #  Full example: disk-changer /var/bacula/conf load 1 /var/bacula/drive0 0 TestVol001
54 #
55 # The Volumes will be created with names slot1, slot2, slot3, ... maxslot in the
56 #  base directory. In the above example the base directory is /var/bacula.
57 #  However, as with tapes, their Bacula Volume names will be stored inside the
58 #  Volume label. In addition to the Volumes (e.g. /var/bacula/slot1,
59 #  /var/bacula/slot3, ...) this script will create a /var/bacula/loadedn
60 #  file to keep track of what Slot is loaded. You should not change this file.
61 #
62 # Modified 8 June 2010 to accept Volume names from the calling program as arg 6.
63 #  In this case, rather than storing the data in slotn, it is stored in the
64 #  Volume name.  Note: for this to work, Volume names may not include spaces.
65 #
66
67 wd=@working_dir@
68
69 #
70 # log whats done
71 #
72 # to turn on logging, uncomment the following line
73 #touch $wd/disk-changer.log
74 #
75 dbgfile="$wd/disk-changer.log"
76 debug() {
77     if test -f $dbgfile; then
78         echo "`date +\"%Y%m%d-%H:%M:%S\"` $*" >> $dbgfile
79     fi
80 }
81
82
83 #
84 # Create a temporary file
85 #
86 make_temp_file() {
87   TMPFILE=`mktemp -t mtx.XXXXXXXXXX`
88   if test x${TMPFILE} = x; then
89      TMPFILE="$wd/disk-changer.$$"
90      if test -f ${TMPFILE}; then
91         echo "Temp file security problem on: ${TMPFILE}"
92         exit 1
93      fi
94   fi
95 }
96
97 # check parameter count on commandline
98 #
99 check_parm_count() {
100     pCount=$1
101     pCountNeed=$2
102     if test $pCount -lt $pCountNeed; then
103         echo "usage: disk-changer ctl-device command [slot archive-device drive-index]"
104         echo "  Insufficient number of arguments arguments given."
105         if test $pCount -lt 2; then
106             echo "  Mimimum usage is first two arguments ..."
107         else
108             echo "  Command expected $pCountNeed arguments"
109         fi
110         exit 1
111     fi
112 }
113
114 #
115 # Strip off the final name in order to get the Directory ($dir)
116 #  that we are dealing with.
117 #
118 get_dir() {
119    bn=`basename $device`
120    dir=`echo "$device" | sed -e s%/$bn%%g`
121    if [ ! -d $dir ]; then
122       echo "ERROR: Autochanger directory \"$dir\" does not exist."
123       echo "       You must create it."
124       exit 1
125    fi
126 }
127
128 #
129 # Get the Volume name from the call line, or directly from
130 #  the volslotn information.
131 #
132 get_vol() {
133    havevol=0
134    debug "vol=$volume"
135    if test "x$volume" != x && test "x$volume" != "x*NONE*" ; then
136       debug "touching $dir/$volume"
137       touch $dir/$volume
138       echo "$volume" >$dir/volslot${slot}
139       havevol=1
140    elif [ -f $dir/volslot${slot} ]; then
141       volume=`cat $dir/volslot${slot}`
142       havevol=1
143    fi
144 }
145
146
147 # Setup arguments
148 ctl=$1
149 cmd="$2"
150 slot=$3
151 device=$4
152 drive=$5
153 volume=$6
154
155 # set defaults
156 maxdrive=1
157 maxslot=10
158
159 # Pull in conf file
160 if [ -f $ctl ]; then
161    . $ctl
162 fi
163
164
165 # Check for special cases where only 2 arguments are needed,
166 #  all others are a minimum of 5
167 #
168 case $2 in
169     list|listall)
170         check_parm_count $# 2
171         ;;
172     slots)
173         check_parm_count $# 2
174         ;;
175     transfer)
176         check_parm_count $# 4
177         if [ $slot -gt $maxslot ]; then
178            echo "Slot ($slot) out of range (1-$maxslot)"
179            debug "Error: Slot ($slot) out of range (1-$maxslot)"
180            exit 1
181         fi
182         ;;
183     *)
184         check_parm_count $# 5
185         if [ $drive -gt $maxdrive ]; then
186            echo "Drive ($drive) out of range (0-$maxdrive)"
187            debug "Error: Drive ($drive) out of range (0-$maxdrive)"
188            exit 1
189         fi
190         if [ $slot -gt $maxslot ]; then
191            echo "Slot ($slot) out of range (1-$maxslot)"
192            debug "Error: Slot ($slot) out of range (1-$maxslot)"
193            exit 1
194         fi
195         ;;
196 esac
197
198
199 debug "Parms: $ctl $cmd $slot $device $drive $volume $havevol"
200
201 case $cmd in
202    unload)
203       debug "Doing disk -f $ctl unload $slot $device $drive $volume"
204       get_dir
205       if [ -f $dir/loaded${drive} ]; then
206          ld=`cat $dir/loaded${drive}`
207       else
208          echo "Storage Element $slot is Already Full"
209          debug "Unload error: $dir/loaded${drive} is already unloaded"
210          exit 1
211       fi
212       if [ $slot -eq $ld ]; then
213          echo "0" >$dir/loaded${drive}
214          unlink $device 2>/dev/null >/dev/null
215       else
216          echo "Storage Element $slot is Already Full"
217          debug "Unload error: $dir/loaded${drive} slot=$ld is already unloaded"
218          exit 1
219       fi
220       ;;
221
222    load)
223       debug "Doing disk $ctl load $slot $device $drive $volume"
224       get_dir
225       i=0
226       # Check if slot already in a drive
227       while [ $i -le $maxdrive ]; do
228          if [ -f $dir/loaded${i} ]; then
229             ld=`cat $dir/loaded${i}`
230          else   
231             ld=0
232          fi
233          if [ $ld -eq $slot ]; then
234             echo "Drive ${i} Full (Storage element ${ld} loaded)"
235             debug "Load error: Cannot load Slot=${ld} in drive=$drive. Already in drive=${i}"
236             exit 1
237          fi
238          i=`expr $i + 1`
239       done
240       # Check if we have a Volume name
241       get_vol
242       if [ $havevol -eq 0 ]; then
243          # check if slot exists
244          if [ ! -f $dir/slot${slot} ] ; then
245             echo "source Element Address $slot is Empty"
246             debug "Load error: source Element Address $slot is Empty"
247             exit 1
248          fi
249       fi
250       if [ -f $dir/loaded${drive} ]; then
251          ld=`cat $dir/loaded${drive}`
252       else
253          ld=0
254       fi
255       if [ $ld -ne 0 ]; then
256          echo "Drive ${drive} Full (Storage element ${ld} loaded)"
257          echo "Load error: Drive ${drive} Full (Storage element ${ld} loaded)"
258          exit 1
259       fi
260       echo "0" >$dir/loaded${drive}
261       unlink $device 2>/dev/null >/dev/null
262       if [ $havevol -ne 0 ]; then
263          ln -s $dir/$volume $device
264          rtn=$?
265       else
266          ln -s $dir/slot${slot} $device
267          rtn=$?
268       fi
269       if [ $rtn -eq 0 ]; then
270          echo $slot >$dir/loaded${drive}
271       fi
272       exit $rtn
273       ;;
274
275    list)
276       debug "Doing disk -f $ctl -- to list volumes"
277       get_dir
278       if [ -f $dir/barcodes ]; then
279          cat $dir/barcodes
280       else
281          i=1
282          while [ $i -le $maxslot ]; do
283             slot=$i
284             volume=
285             get_vol
286             if [ $havevol -eq 0 ]; then
287                echo "$i:"
288             else
289                echo "$i:$volume"
290             fi
291             i=`expr $i + 1`
292          done
293       fi
294       exit 0
295       ;;
296
297    listall)
298       # ***FIXME*** must add new Volume stuff
299       make_temp_file
300       debug "Doing disk -f $ctl -- to list volumes"
301       get_dir
302       if [ ! -f $dir/barcodes ]; then
303           exit 0
304       fi
305
306       # we print drive content seen by autochanger
307       # and we also remove loaded media from the barcode list
308       i=0
309       while [ $i -le $maxdrive ]; do
310          if [ -f $dir/loaded${i} ]; then
311              ld=`cat $dir/loaded${i}`
312              v=`awk -F: "/^$ld:/"' { print $2 }' $dir/barcodes`
313              echo "D:$i:F:$ld:$v"
314              echo "^$ld:" >> $TMPFILE
315          fi
316          i=`expr $i + 1`
317       done
318
319       # Empty slots are not in barcodes file
320       # When we detect a gap, we print missing rows as empty
321       # At the end, we fill the gap between the last entry and maxslot
322       grep -v -f $TMPFILE $dir/barcodes | sort -n | \
323       perl -ne 'BEGIN { $cur=1 }
324        if (/(\d+):(.+)?/) {
325          if ($cur == $1) {
326            print "S:$1:F:$2\n"
327          } else {
328            while ($cur < $1) {
329               print "S:$cur:E\n";
330               $cur++;
331            }
332          }
333          $cur++;
334        }
335        END { while ($cur < '"$maxslot"') { print "S:$cur:E\n"; $cur++; } } '
336
337       rm -f $TMPFILE
338       exit 0
339       ;;
340    transfer)
341       #  ***FIXME*** must add new Volume stuff
342       get_dir
343       make_temp_file
344       slotdest=$device
345       if [ -f $dir/slot{$slotdest} ]; then
346          echo "destination Element Address $slot is Full"
347          exit 1
348       fi
349       if [ ! -f $dir/slot${slot} ] ; then
350          echo "source Element Address $slot is Empty"
351          exit 1
352       fi
353
354       echo "Transfering $slot to $slotdest"
355       mv $dir/slot${slot} $dir/slot{$slotdest}
356
357       if [ -f $dir/barcodes ]; then
358          sed "s/^$slot:/$slotdest:/" >  $TMPFILE
359          sort -n $TMPFILE > $dir/barcodes
360       fi
361       exit 0
362       ;;
363    loaded)
364       debug "Doing disk -f $ctl $drive -- to find what is loaded"
365       get_dir
366       if [ -f $dir/loaded${drive} ]; then
367          a=`cat $dir/loaded${drive}`
368       else
369          a="0"
370       fi
371       debug "Loaded: drive=$drive is $a"
372       echo $a
373       exit
374       ;;
375
376    slots)
377       debug "Doing disk -f $ctl -- to get count of slots"
378       echo $maxslot
379       ;;
380 esac