disk-changer 11 KB

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