/etc/bacula/scripts/disk-changer is in bacula-sd 5.2.6+dfsg-9.1ubuntu3.
This file is owned by root:root, with mode 0o755.
The actual contents of the file can be viewed below.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 | #!/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
 |