1#!/bin/sh
2#
3# Bareos interface to mtx autoloader
4#
5#  If you set in your Device resource
6#
7#  Changer Command = "path-to-this-script/mtx-changer %c %o %S %a %d"
8#    you will have the following input to this script:
9#
10#  So Bareos will always call with all the following arguments, even though
11#    in come cases, not all are used.
12#
13#  mtx-changer "changer-device" "command" "slot" "archive-device" "drive-index"
14#		   $1		   $2	    $3	      $4	       $5
15#
16#  for example:
17#
18#  mtx-changer /dev/sg0 load 1 /dev/nst0 0 (on a Linux system)
19#
20#  will request to load the first cartidge into drive 0, where
21#   the SCSI control channel is /dev/sg0, and the read/write device
22#   is /dev/nst0.
23#
24#  The commands are:
25#      Command		  Function
26#      unload		  unload a given slot
27#      load		  load a given slot
28#      loaded		  which slot is loaded?
29#      list		  list Volume names (requires barcode reader)
30#      slots		  how many slots total?
31#      listall		  list all info
32#      transfer
33#
34#  Slots are numbered from 1 ...
35#  Drives are numbered from 0 ...
36#
37#
38#  If you need to an offline, refer to the drive as $4
39#    e.g.   mt -f $4 offline
40#
41#  Many changers need an offline after the unload. Also many
42#   changers need a sleep 60 after the mtx load.
43#
44#  N.B. If you change the script, take care to return either
45#   the mtx exit code or a 0. If the script exits with a non-zero
46#   exit code, Bareos will assume the request failed.
47#
48
49# source our conf file
50if test ! -r @confdir@/mtx-changer.conf ; then
51  echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
52  echo "ERROR: @confdir@/mtx-changer.conf file not found!!!!"
53  echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
54  exit 1
55fi
56. @confdir@/mtx-changer.conf
57
58MTX=@MTX@
59if test ! -x "$MTX" ; then
60  echo "ERROR: mtx program not found ($MTX)"
61  exit 1
62fi
63
64dbgfile="@logdir@/mtx.log"
65if test ${debug_log} -ne 0 ; then
66  touch $dbgfile
67fi
68debug() {
69    if test -f $dbgfile; then
70        echo "`date +\"%Y%m%d-%H:%M:%S\"` $*" >> $dbgfile
71    fi
72}
73
74
75#
76# Create a temporary file
77#
78make_temp_file() {
79  TMPFILE=`mktemp @working_dir@/mtx.XXXXXXXXXX`
80  if test x${TMPFILE} = x; then
81     TMPFILE="@working_dir@/mtx.$$"
82     if test -f ${TMPFILE}; then
83	echo "ERROR: Temp file security problem on: ${TMPFILE}"
84	exit 1
85     fi
86  fi
87}
88
89#
90#  Create a temporary file for stderr
91#
92#  Note, this file is used because sometime mtx emits
93#  unexpected error messages followed by the output
94#  expected during success.
95#  So we separate STDOUT and STDERR in
96#  certain of the mtx commands. The contents of STDERR
97#  is then printed after the STDOUT produced by mtx
98#  thus we sometimes get better changer results.
99#
100make_err_file() {
101  ERRFILE=`mktemp @working_dir@/mtx.err.XXXXXXXXXX`
102  if test x${ERRFILE} = x; then
103     ERRFILE="@working_dir@/mtx.err.$$"
104     if test -f ${ERRFILE}; then
105	echo "ERROR: Temp file security problem on: ${ERRFILE}"
106	exit 1
107     fi
108  fi
109}
110
111
112#
113# The purpose of this function to wait a maximum
114#   time for the drive. It will
115#   return as soon as the drive is ready, or after
116#   waiting a maximum of 300 seconds.
117# Note, this is very system dependent, so if you are
118#   not running on Linux, you will probably need to
119#   re-write it, or at least change the grep target.
120#   We've attempted to get the appropriate OS grep targets
121#   in the code at the top of this script.
122#
123wait_for_drive() {
124  i=0
125  while [ $i -le 300 ]; do  # Wait max 300 seconds
126    if mt -f $1 status 2>&1 | grep "${ready}" >/dev/null 2>&1; then
127      break
128    fi
129    debug "Device $1 - not ready, retrying..."
130    sleep 1
131    i=`expr $i + 1`
132  done
133}
134
135# check parameter count on commandline
136#
137check_parm_count() {
138    pCount=$1
139    pCountNeed=$2
140    if test $pCount -lt $pCountNeed; then
141	echo "ERROR: usage: mtx-changer ctl-device command [slot archive-device drive-index]"
142	echo "	Insufficient number of arguments given."
143	if test $pCount -lt 2; then
144	    echo "  Mimimum usage is first two arguments ..."
145	else
146	    echo "  Command expected $pCountNeed arguments"
147	fi
148	exit 1
149    fi
150}
151
152# Check for special cases where only 2 arguments are needed,
153#  all others are a minimum of 5
154#
155case $2 in
156    list|listall)
157	check_parm_count $# 2
158	;;
159    slots)
160	check_parm_count $# 2
161	;;
162    transfer)
163	check_parm_count $# 4
164	;;
165    *)
166	check_parm_count $# 5
167	;;
168esac
169
170
171# Setup arguments
172ctl=$1
173cmd="$2"
174slot=$3
175device=$4
176drive=$5
177
178debug "Parms: $ctl $cmd $slot $device $drive"
179
180case $cmd in
181   unload)
182      debug "Doing mtx -f $ctl unload $slot $drive"
183
184      if test ${offline} -eq 1 ; then
185        mt -f $device offline
186      fi
187      if test ${offline_sleep} -ne 0 ; then
188        sleep ${offline_sleep}
189      fi
190      make_err_file
191      ${MTX} -f $ctl unload $slot $drive 2>${ERRFILE}
192      rtn=$?
193      cat ${ERRFILE}
194      rm -f ${ERRFILE} >/dev/null 2>&1
195      exit $rtn
196      ;;
197
198   load)
199      debug "Doing mtx -f $ctl load $slot $drive"
200      make_err_file
201      ${MTX} -f $ctl load $slot $drive 2>${ERRFILE}
202      rtn=$?
203      if test ${load_sleep} -ne 0 ; then
204        sleep ${load_sleep}
205      fi
206      wait_for_drive $device
207      cat ${ERRFILE}
208      rm -f ${ERRFILE} >/dev/null 2>&1
209      exit $rtn
210      ;;
211
212   list)
213      debug "Doing mtx -f $ctl -- to list volumes"
214      make_temp_file
215      if test ${inventory} -ne 0 ; then
216        ${MTX} -f $ctl inventory
217      fi
218      ${MTX} -f $ctl status >${TMPFILE}
219      rtn=$?
220      if test ${vxa_packetloader} -ne 0 ; then
221        cat ${TMPFILE} | grep " *Storage Element [0-9]*:.*Full" | sed "s/ Storage Element //" | sed "s/Full :VolumeTag=//"
222      else
223	cat ${TMPFILE} | grep " Storage Element [0-9]*:.*Full" | awk "{print \$3 \$4}" | sed "s/Full *\(:VolumeTag=\)*//"
224      fi
225      cat ${TMPFILE} | grep "^Data Transfer Element [0-9]*:Full (Storage Element [0-9]" | awk '{printf "%s:%s\n",$7,$10}'
226      rm -f ${TMPFILE} >/dev/null 2>&1
227      exit $rtn
228      ;;
229
230   listall)
231#  Drive content:	  D:Drive num:F:Slot loaded:Volume Name
232#  D:0:F:2:vol2        or D:Drive num:E
233#  D:1:F:42:vol42
234#  D:3:E
235#
236#  Slot content:
237#  S:1:F:vol1		  S:Slot num:F:Volume Name
238#  S:2:E	       or S:Slot num:E
239#  S:3:F:vol4
240#
241#  Import/Export tray slots:
242#  I:10:F:vol10 	  I:Slot num:F:Volume Name
243#  I:11:E	       or I:Slot num:E
244#  I:12:F:vol40
245
246      debug "Doing mtx -f $ctl -- to list all"
247      make_temp_file
248      if test ${inventory} -ne 0 ; then
249        ${MTX} -f $ctl inventory
250      fi
251      ${MTX} -f $ctl status >${TMPFILE}
252      rtn=$?
253      # can be converted to awk+sed+cut, see below
254      perl -ne '
255/Data Transfer Element (\d+):Empty/ && print "D:$1:E\n";
256/Data Transfer Element (\d+):Full \(Storage Element (\d+) Loaded\)(:VolumeTag =\s*(.+))?/ && print "D:$1:F:$2:$4\n";
257/Storage Element (\d+):Empty/ && print "S:$1:E\n";
258/Storage Element (\d+):Full( :VolumeTag=(.+))?/ && print "S:$1:F:$3\n";
259/Storage Element (\d+) IMPORT.EXPORT:Empty/ && print "I:$1:E\n";
260/Storage Element (\d+) IMPORT.EXPORT:Full( :VolumeTag=(.+))?/ && print "I:$1:F:$3\n";' ${TMPFILE}
261      # If perl isn't installed, you can use by those commands
262#cat ${TMPFILE} | grep "Data Transfer Element" | awk "{print \"D:\"\$4 \$7 \$9 \$10}" | sed "s/=/:/" | sed "s/Full/F:/" | sed "s/Empty/E/"
263#cat ${TMPFILE} | grep -v "Data Transfer Element" | grep "Storage Element" | grep -v "IMPORT/EXPORT" | awk "{print \"S:\"\$3 \$4 \$5}" | sed "s/IMPORT\/EXPORT//" | sed "s/Full *:VolumeTag=/F:/" | sed "s/Empty/E/"
264#cat ${TMPFILE} | grep -v "Data Transfer Element" | grep "Storage Element" | grep "IMPORT/EXPORT" | awk "{print \"I:\"\$3 \$4 \$5}" | sed "s/IMPORT\/EXPORT//" | sed "s/Full *:VolumeTag=/F:/" | sed "s/Empty/E/"
265
266      rm -f ${TMPFILE} >/dev/null 2>&1
267      exit $rtn
268      ;;
269
270   transfer)
271      slotdest=$device
272      debug "Doing transfer from $slot to $slotdest"
273      ${MTX} -f $ctl transfer $slot $slotdest
274      rtn=$?
275      exit $rtn
276      ;;
277
278   loaded)
279      debug "Doing mtx -f $ctl $drive -- to find what is loaded"
280      make_temp_file
281      ${MTX} -f $ctl status >${TMPFILE}
282      rtn=$?
283      cat ${TMPFILE} | grep "^Data Transfer Element $drive:Full" | awk "{print \$7}"
284      cat ${TMPFILE} | grep "^Data Transfer Element $drive:Empty" | awk "{print 0}"
285      rm -f ${TMPFILE} >/dev/null 2>&1
286      exit $rtn
287      ;;
288
289   slots)
290      debug "Doing mtx -f $ctl -- to get count of slots"
291      ${MTX} -f $ctl status | grep " *Storage Changer" | awk "{print \$5}"
292      ;;
293esac
294