Fix locking and command status output
[blkdevalias.git] / blkdevalias
1 #!/bin/bash
2 #
3 #  blkdevalias - manage aliases and permissions for block devices
4 #
5 #  Bryn M. Reeves <bmr@redhat.com>
6 #
7 #  Copyright (C) Red Hat, Inc. 2012, 2013
8 #
9 #  This program is free software; you can redistribute it and/or modify
10 #  it under the terms of the GNU General Public License as published by
11 #  the Free Software Foundation; either version 2 of the License, or
12 #  (at your option) any later version.
13 #
14 #  This program is distributed in the hope that it will be useful,
15 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 #  GNU General Public License for more details.
18 #
19 #  You should have received a copy of the GNU General Public License
20 #  along with this program; if not, write to the Free Software
21 #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22 #
23
24 # global associative arrays containing the device map
25 declare -A WWIDMAP
26 declare -A NAMEMAP
27 declare -A TYPEMAP
28 declare -A PARTMAP
29
30 # To enable debug output set the BA_DEBUG environment variable to "yes".
31 #BA_DEBUG="yes"
32
33 # defaults
34 BA_DEFAULT_USER="oracle"
35 BA_DEFAULT_GROUP="oracle"
36 BA_DEFAULT_MODE="0660"
37
38 # paths and configuration files
39 BA_CONF_DIR="/etc/blkdevalias"
40 BA_DEV_PATH="oracleasm/disks"
41 BA_MAP_PATH="${BA_CONF_DIR}/blkdevalias.map"
42 BA_CONF_PATH="${BA_CONF_DIR}/blkdevalias.conf"
43 BA_LOCK_PATH="/var/lock/blkdevalias"
44
45 # locking
46 BA_LOCK_FD=9
47
48 # user and group - only used if loading with a blank configuration file.
49 BA_USER="$BA_DEFAULT_USER"
50 BA_GROUP="$BA_DEFAULT_GROUP"
51
52 # programs used by blkdevalias
53 BA_SCSI_ID_FLAGS="-g -u"
54 BA_SCSI_ID="/sbin/scsi_id"
55 BA_READLINK="/usr/bin/readlink"
56 BA_BASENAME="/bin/basename"
57 BA_DMSETUP="/sbin/dmsetup"
58 BA_GETENT="/usr/bin/getent"
59 BA_CHOWN="/bin/chown"
60 BA_FLOCK="/usr/bin/flock"
61 BA_SUDO="/usr/bin/sudo"
62 BA_SED="/bin/sed"
63 BA_ID="/usr/bin/id"
64 BA_SH="/bin/bash"
65
66 # real user and group ID the script is running as
67 BA_REAL_UID="$($BA_ID -u)"
68 BA_REAL_GID="$($BA_ID -g)"
69
70 # load config
71 . $BA_CONF_PATH
72
73 _print () {
74     echo -e "$@"
75 }
76
77 _error () {
78     echo -e "ERROR: $@" 1>&2
79 }
80
81 _warn () {
82     echo -e "WARNING: $@" 1>&2
83 }
84
85 _debug () {
86     [ "$BA_DEBUG" == "yes" ] && echo -e "$@" 1>&2
87 }
88
89 # print usage message
90 # sub-commands may provide their own usage messages
91 ba_usage () {
92     if [ x"$@" == x"" ]; then
93         _print "Usage: $0 <command> [<arg>]..."
94     else
95         _print "Usage: $0 $@"
96     fi
97     exit 2
98 }
99
100 ba_store_wwid_map_header() {
101     local _TMP_MAP="$1"
102     cat > $_TMP_MAP << EOF
103 # THIS FILE IS AUTOMATICALLY GENERATED. MANUAL EDITS
104 # WILL BE LOST WHEN ALIASES ARE ADDED OR REMOVED.
105 # <WWID> <ALIAS> <TYPE> <PARTNUM>
106 EOF
107 }
108
109 # store the WWID map to disk
110 ba_store_wwid_map() {
111     local _WWID _TMP_MAP
112     _TMP_MAP=$(mktemp --tmpdir=$BA_CONF_DIR)
113     if [ ! -f "$_TMP_MAP" ]; then
114         _error "could not write temporary file"
115         exit 1
116     fi
117     ba_store_wwid_map_header $_TMP_MAP
118     for _WWID in ${!WWIDMAP[@]}; do
119         echo "$_WWID ${WWIDMAP[$_WWID]}" \
120                 "${TYPEMAP[$_WWID]} ${PARTMAP[$_WWID]}"
121     done  >> $_TMP_MAP
122     mv $_TMP_MAP $BA_MAP_PATH
123     # we may be running as root - if so re-chown the configs
124     if [ "$BA_REAL_UID" == "0" ] || [ "$BA_REAL_GID" == "0" ]; then
125         ba_chown_configs $BA_USER $BA_GROUP
126     fi
127 }
128
129 # read the WWID map from disk
130 ba_load_wwid_map() {
131     local _WWID _ALIAS _TYPE _PART
132     if [ ! -f "$BA_MAP_PATH" ] || [ ! -r "$BA_MAP_PATH"  ]; then
133         _error "$BA_MAP_PATH does not exist or is not readable"
134         exit 1
135     fi
136     while read _WWID _ALIAS _TYPE _PART; do
137         if [[ $_WWID == \#* ]] || [[ $_WWID == "" ]]; then
138             continue
139         fi
140         WWIDMAP[$_WWID]="$_ALIAS"
141         NAMEMAP[$_ALIAS]="$_WWID"
142         TYPEMAP[$_WWID]="$_TYPE"
143         PARTMAP[$_WWID]="$_PART"
144     done < $BA_MAP_PATH
145 }
146
147 # ba_add_wwid_mapping <wwid> <alias> <sd|mpath> <part>
148 ba_add_wwid_mapping() {
149     local _WWID _ALIAS _TYPE _PART _DEV
150     _WWID="$1"
151     _ALIAS="$2"
152     _TYPE="$3"
153     _PART="$4"
154     _DEV="$5"
155     _debug "$FUNCNAME: WWID=\"$_WWID\" ALIAS=\"$_ALIAS\"" \
156         "TYPE=\"$_TYPE\" PART=\"$_PART\""
157     if [ "$_WWID" == "" ]; then
158         _warn "no wwid given"
159         return
160     fi
161     if [ "$_ALIAS" == "" ]; then
162         _warn "no alias given"
163         return
164     fi
165     if [ "$_PART" == "" ]; then
166         _warn "no partition number given"
167     fi
168     if [ "${WWIDMAP[$_WWID]}" != "" ]; then
169         _debug "$FUNCNAME: WWIDMAP[\$WWID]=${WWIDMAP[$_WWID]}"
170         _warn "$DEV already mapped to alias ${WWIDMAP[$_WWID]}; replacing it."
171         ba_del_wwid_mapping "$_WWID"
172     fi
173     if [ "${NAMEMAP[$_ALIAS]}" != "" ]; then
174         _debug "$FUNCNAME: NAMEMAP[\$ALIAS]=${NAMEMAP[$_ALIAS]}"
175         _warn "$DEV already mapped to disk ${NAMEMAP[$_ALIAS]}; replacing it."
176         ba_del_wwid_mapping ${NAMEMAP[$_ALIAS]}
177     fi
178     WWIDMAP[$_WWID]="$_ALIAS"
179     NAMEMAP[$_ALIAS]="$_WWID"
180     TYPEMAP[$_WWID]=${_TYPE:-"sd"}
181     PARTMAP[$_WWID]="$_PART"
182 }
183
184 # ba_del_wwid_mapping <wwid>
185 ba_del_wwid_mapping() {
186     local _WWID _ALIAS
187     _WWID="$1"
188     if [ "$_WWID" == "" ]; then
189         return 1
190     fi
191     if [ "${WWIDMAP[$_WWID]}" == "" ]; then
192         _warn "WARNING: $_WWID does not exist; doing nothing."
193         return 1
194     fi
195     _debug "\nba_del_wwid_mapping: unsetting WWIDMAP[$_WWID]"
196     _ALIAS=${WWIDMAP[$_WWID]}
197     unset WWIDMAP[$_WWID]
198     unset NAMEMAP[$_ALIAS]
199     unset TYPEMAP[$_WWID]
200     unset PARTMAP[$_WWID]
201 }
202
203 # ba_get_partnum <device>
204 ba_get_partnum () {
205     local _DEV _NUM
206     _DEV="$1"
207     if [[ $_DEV == dm-* ]]; then
208         _DEV=$DM_NAME
209     fi
210     _debug "$FUNCNAME: $_DEV"
211     _debug "$FUNCNAME: calling $BA_DMSETUP info $_DEV"
212     $BA_DMSETUP info $_DEV > /dev/null 2>&1
213     if [ "$?" == "0" ]; then
214         _NUM=$(echo $_DEV | sed -e 's/\/dev.*\///' -e 's/.*p\([0-9]*\).*/\1/')
215         _debug "$FUNCNAME: dm device $_DEV NUM=$_NUM"
216     else
217         _NUM=$(echo $_DEV | sed 's/\(\/dev\/\)*sd[a-z]*//')
218         _debug "$FUNCNAME: non-dm device $_DEV NUM=$_NUM"
219     fi
220     if [ "$_NUM" == "" ]; then
221         _NUM="0"
222     fi
223     _print $_NUM
224 }
225
226 # ba_scsi_id: <device path>
227 ba_scsi_id () {
228     local _DEV
229     _DEV="$1"
230     _debug "$FUNCNAME: getting ID for device $_DEV"
231     $BA_DMSETUP info $_DEV > /dev/null  2>&1
232     if [ $? -eq 0 ]; then
233         _DEV=$(echo $_DEV | sed 's/p[0-9].*//')
234     else
235         _DEV=$(echo $_DEV | sed 's/[0-9].*//')
236     fi
237      _debug "$FUNCNAME: calling \"$BA_SCSI_ID $BA_SCSI_ID_FLAGS $_DEV\""
238     $BA_SUDO $BA_SCSI_ID $BA_SCSI_ID_FLAGS $_DEV
239 }
240
241 # ba_chown_configs <user> <group>
242 ba_chown_configs () {
243     local _USER _GROUP _OUT
244     _USER="$1"
245     _GROUP="$2"
246     $BA_CHOWN -R "$_USER:$_GROUP" "$BA_CONF_DIR"
247     ls -l "$BA_CONF_DIR" | while read _OUT; do
248         _debug "$FUNCNAME: $_OUT"
249     done
250     return 0
251 }
252
253 # ba_refresh_alias <alias>
254 ba_refresh_alias () {
255     local _ALIAS _DEVPATH
256     _ALIAS="$1"
257     _DEVPATH="/dev/$BA_DEV_PATH/$_ALIAS"    
258     _debug "$FUNCNAME: ALIAS=$_ALIAS DEVPATH=$_DEVPATH"
259     ba_refresh_device $_DEVPATH
260 }
261
262 # ba_refresh_device <dev path>
263 ba_refresh_device () {
264     local _DEVPATH _DEVNAME _LINKDEST _PARTNUM
265     _DEVPATH="$1"
266     _debug "$FUNCNAME: refreshing device at path $_DEVPATH"
267     _LINKDEST=$($BA_READLINK $_DEVPATH)
268     if [ "$?" != 0 ]; then
269         _DEVNAME=$($BA_BASENAME $_DEVPATH | $BA_SED 's/p\+[1-9]*$//')
270         _PARTNUM=$(ba_get_partnum $_DEVNAME)
271         if [ "$_PARTNUM" != "0" ]; then
272             _DEVNAME="$(echo ${_DEVNAME} | sed 's/[0-9]*$//')/$_DEVNAME"
273         fi
274         _debug "$FUNCNAME: non-symlinked dev at" \
275             "$_DEVPATH has name $_DEVNAME"
276     else
277         _DEVNAME=$($BA_BASENAME $_LINKDEST)
278         _debug "$FUNCNAME: symlinked dev at" \
279             "$_DEVPATH has name $_DEVNAME"
280     fi
281     _debug "$FUNCNAME: requesting change uevent at" \
282             "sysfs path /sys/block/$_DEVNAME"
283     $BA_SUDO $BA_SH -c "echo change > /sys/block/$_DEVNAME/uevent"
284 }
285
286 #ba_configure_store_header: <file>
287 ba_store_conf_header() {
288     local _TMP_CONF="$1"
289     cat > $_TMP_CONF <<EOF
290 # THIS FILE IS AUTOMATICALLY GENERATED. MANUAL EDITS
291 # WILL BE LOST WHEN THE CONFIGURE COMMAND IS ISSUED.
292 # SEE BLKDEVALIAS.CONF(5) FOR CONFIGURATION PARAMETERS.
293 EOF
294 }
295
296 # ba_configure:
297 ba_configure () {
298     local _BA_USER _BA_GROUP _BA_DEV_PATH _TMP_CONF
299     cat <<EOF
300 Configuring the blkdevalias map.
301
302 This will configure persistent aliases and permissions for storage
303 devices. The current values will be shown in brackets ('[]'). Hitting
304 <ENTER> without typing an answer will keep the current value.
305
306 Ctrl-C will abort.
307
308 EOF
309
310     _print -n "Default user to own device nodes [$BA_USER]: "
311     read _BA_USER
312     if [ "$_BA_USER" != "" ]; then
313         BA_USER=$_BA_USER;
314     fi
315     $BA_GETENT passwd "$BA_USER" &>/dev/null
316     if [ "$?" != 0 ]; then
317         _error "user (\"$BA_USER\") must be a valid user account"
318         exit 1
319     fi
320
321     _print -n "Default group to own device nodes [$BA_GROUP]: "
322     read _BA_GROUP
323     if [ "$_BA_GROUP" != "" ]; then
324         BA_GROUP=$_BA_GROUP;
325     fi
326     $BA_GETENT group "$BA_GROUP" &>/dev/null
327     if [ "$?" != 0 ]; then
328         _error "user (\"$BA_GROUP\") must be a valid group account"
329         exit 1
330     fi
331     _print -n "Default device directory [$BA_DEV_PATH]: "
332     read _BA_DEV_PATH
333     if [ "$_BA_DEV_PATH" != "" ]; then
334         BA_DEV_PATH=$_BA_DEV_PATH;
335     fi
336
337     _debug "BA_DEV_PATH=\"$BA_DEV_PATH\""
338     _debug "BA_USER=\"$BA_USER\""
339     _debug "BA_GROUP=\"$BA_GROUP\""
340     _print -n "Writing wwid map configuration: "
341     _TMP_CONF=$(mktemp --tmpdir=$BA_CONF_DIR)
342     if [ ! -f "$_TMP_CONF" ]; then
343         _print
344         _error "could not write temporary file"
345         exit 1
346     fi
347     ba_store_conf_header $_TMP_CONF
348     echo "BA_DEV_PATH=\"$BA_DEV_PATH\"" >> $_TMP_CONF
349     echo "BA_USER=\"$BA_USER\"" >> $_TMP_CONF
350     echo "BA_GROUP=\"$BA_GROUP\"" >> $_TMP_CONF
351     echo "BA_MODE=\"$BA_MODE\"" >> $_TMP_CONF
352     mv $_TMP_CONF $BA_CONF_PATH
353     if [ "$?" != 0 ]; then
354         _error "could not create configuration file $BA_CONF_PATH"
355         exit
356     fi
357     # set configration ownership to reflect admin users
358     ba_chown_configs "$BA_USER" "$BA_GROUP"
359     _print -e "done\n"
360 }
361
362 # ba_createdisk <alias> <device>
363 ba_createdisk () {
364     local _WWID _DEV _ALIAS _TYPE _PART
365     _ALIAS="$1"
366     _DEV="$2"
367     if [ "$_DEV" == "" ] || [ "$_ALIAS" == "" ]; then
368         ba_usage "createdisk <alias> <device>"
369     fi
370     _debug "$FUNCNAME: DEV=\"$_DEV\" ALIAS=\"$_ALIAS\""
371
372     _WWID=$(ba_scsi_id $_DEV)
373     if [ "$_WWID" == "" ]; then
374         _error "could not get SCSI ID for $_DEV"
375         exit 1
376     fi
377     _debug "$FUNCNAME: WWID=\"$_WWID\""
378
379     if $BA_DMSETUP info $_DEV > /dev/null  2>&1; then
380         _TYPE="mpath"
381     else
382         _TYPE="sd"
383     fi
384
385     _PART=$(ba_get_partnum $_DEV) 
386     _debug "$FUNCNAME: PART=\"$_PART\""
387
388     ba_add_wwid_mapping $_WWID $_ALIAS $_TYPE $_PART $_DEV
389     ba_store_wwid_map
390     ba_refresh_device $_DEV
391 }
392
393 # ba_deletedisk: <alias>
394 ba_deletedisk () {
395     local _WWID _ALIAS _DEVPATH
396     _ALIAS=$1
397     if [ "$_ALIAS" == "" ]; then
398         ba_usage "deletedisk <alias>"
399     fi
400     _WWID=${NAMEMAP[$_ALIAS]}
401     if [ "$_WWID" == "" ]; then
402         _error "\"$_ALIAS\" does not exist"
403         ba_usage "deletedisk <alias>"
404     fi
405     # generate device path for refresh call
406     _DEVPATH="/dev/$BA_DEV_PATH/${WWIDMAP[$_WWID]}"
407     _TARGET=$($BA_READLINK -f $_DEVPATH)
408     if [ "$_DEVPATH" == "$_TARGET" ]; then
409         _warn "device at $_DEVPATH not found;" \
410                 "will not update device mappings"
411     fi
412     _debug "$FUNCNAME: removing wwid mapping for" \
413             "WWID=\"$WWID\" DEVPATH=\"$_DEVPATH\" TARGET=\"$_TARGET\""
414     echo -n "Removing wwid map disk \"$_ALIAS\":"
415     ba_del_wwid_mapping $_WWID \
416     && if [ "$BA_DEBUG" != "yes" ]; then
417         echo "                                  [  OK  ]"
418     fi
419     ba_store_wwid_map
420     if [ "$_TARGET" != "$_DEVPATH" ]; then
421         ba_refresh_device $_TARGET
422     fi
423 }
424
425 # ba_list_disks:
426 ba_list_disks () {
427     local _D
428     for _D in ${WWIDMAP[@]}; do
429         local _DEVPATH
430         _DEVPATH="/dev/$BA_DEV_PATH/${WWIDMAP[${NAMEMAP[$_D]}]}"
431         echo -n $_D " $_DEVPATH -> "
432         echo $($BA_READLINK $_DEVPATH)
433     done | sort -V
434 }
435
436 # ba_querydisk: <[-d]>
437 ba_querydisk () {
438     local _LISTDEV _ONDEV _WWID _ALIAS
439     if [ "$1" == "-d" ]; then
440         _LISTDEV=1
441         shift
442     fi
443     _ALIAS=$1
444     if [ "$_ALIAS" == "" ]; then
445         ba_usage "querydisk <alias>"
446         exit 2
447     fi
448     _WWID=${NAMEMAP[$_ALIAS]}
449     _debug "$FUNCNAME: ALIAS=\"$_ALIAS\"" \
450         "WWID=\"$_WWID\" LISTDEV=\"$_LISTDEV\""
451     if [ "$_LISTDEV" == "1" ]; then
452         DEV="/dev/$BA_DEV_PATH/$($BA_READLINK "/dev/$BA_DEV_PATH/$_ALIAS")"
453         MAJ="$[0x$(stat --format "%t" $DEV)]"
454         MIN="$[0x$(stat --format "%T" $DEV)]"
455         _ONDEV="on device [$MAJ,$MIN]"
456         _debug "$FUNCNAME: DEV=\"$DEV\" MAJ=\"$MAJ\" MIN=\"$MIN\""
457     fi
458     if [ "$_WWID" != "" ]; then
459         echo "$_ALIAS is a valid blkdevalias disk $_ONDEV"
460         return
461     fi
462     exit 1
463 }
464
465 ba_scandisks () {
466     local _DEV _TARGET _REFRESH
467     if [ "x$1" == "x-r" ]; then
468         _REFRESH="yes"
469     else
470         _REFRESH="no"
471     fi
472     _debug "$FUNCNAME: device refresh requested: $_REFRESH"
473     for _DEV in /dev/$BA_DEV_PATH/*; do
474         _TARGET=$($BA_READLINK $_DEV)
475         # perform refresh on pre-canonicalised device path
476         if [ "$_REFRESH" == "yes" ]; then
477             _debug "$FUNCNAME: refreshing device at $_TARGET"
478             ba_refresh_device $_TARGET
479         fi
480         if [[ $_TARGET == *dm-* ]]; then
481             _TARGET=$($BA_SUDO $BA_DMSETUP info \
482                 --noheadings -c /dev/$(basename $_TARGET) -o name)
483         else
484             _TARGET=$(basename $_TARGET)
485         fi
486         _print "$_DEV ${NAMEMAP[$(basename $_DEV)]} $_TARGET"
487     done
488 }
489
490 # ba_map: <wwid>
491 # DEVNAME - kernel name (supplied by udev)
492 # ID_BUS  - bus ID string (supplied by udev)
493 ba_map () {
494     local _WWID _TYPE _PART
495     _WWID="$1"
496     _debug "$FUNCNAME: $_WWID -> ${WWIDMAP[$_WWID]}"
497     if [ "$_WWID" == "" ] || [ "${WWIDMAP[$_WWID]}" == "" ]; then
498         ba_usage "map <wwid>"
499         exit 2
500     fi
501     _PART=$(ba_get_partnum $DEVNAME)
502     if [ "$ID_BUS" == "scsi" ]; then
503         _TYPE=sd
504     else
505         _TYPE=mpath
506     fi
507     _debug "$FUNCNAME: WWID=$_WWID PART=$_PART TYPE=$_TYPE"
508     if [ "$_PART" != ${PARTMAP[$_WWID]} ] \
509         || [ "$_TYPE" != ${TYPEMAP[$_WWID]} ] ; then
510         exit 1
511     fi
512     echo "BA_WWID=$_WWID"
513     echo "BA_NAME=$BA_DEV_PATH/${WWIDMAP[$_WWID]}"
514     echo "BA_TYPE=${TYPEMAP[$_WWID]}"
515     echo "BA_USER=$BA_USER"
516     echo "BA_GROUP=$BA_GROUP"
517     echo "BA_MODE=$BA_MODE"
518 }
519
520 ba_lock_map () {
521     local _MODE _FLAG
522     _MODE="$1"
523     _FLAG="-s" # shared by default
524     if [ "$_MODE" == "exclusive" ]; then
525         _FLAG="-x"
526     fi
527     _debug "$FUNCNAME: locking $BA_LOCK_PATH on" \
528         "fd $BA_LOCK_FD with mode: $_MODE"
529     $BA_FLOCK $_FLAG -n $BA_LOCK_FD
530     _STATUS="$?"
531     _debug "$FUNCNAME: flock exited with status $?"
532     return $_STATUS
533 }
534
535 ba_unlock_map () {
536     local _STATUS
537     _debug "$FUNCNAME: unlocking $BA_LOCK_PATH on fd $BA_LOCK_FD"
538     $BA_FLOCK -u -n $BA_LOCK_FD
539     _STATUS="$?"
540     _debug "$FUNCNAME: flock exited with status $_STATUS"
541     return $_STATUS
542 }
543
544 # no point optimizing this out for configure
545 ba_load_wwid_map
546 _debug "ba_main: dispatching command \"$@\""
547 case "$1" in
548     configure)
549         ba_configure
550         ;;
551     create*)
552         (
553             # lock exclusive
554             ba_lock_map exclusive || _error "could not acquire read lock" \
555                  "on $BA_LOCK_PATH"
556             ba_createdisk "$2" "$3"
557             ba_unlock_map
558         ) 9> $BA_LOCK_PATH
559         ;;
560     delete*)
561         (
562             # lock exclusive
563             ba_lock_map exclusive || _error "could not acquire read lock" \
564                  "on $BA_LOCK_PATH"
565             ba_deletedisk "$2"
566             ba_unlock_map
567         ) 9> $BA_LOCK_PATH
568         ;;
569     list*)
570         (
571             # lock shared
572             ba_lock_map shared || _error "could not acquire read lock" \
573                  "on $BA_LOCK_PATH"
574             ba_list_disks
575             ba_unlock_map
576         ) 9> $BA_LOCK_PATH
577         ;;
578     query*)
579         (
580             # lock shared
581             ba_lock_map shared || _error "could not acquire read lock" \
582                  "on $BA_LOCK_PATH"
583             ba_querydisk "$2" "$3"
584             ba_unlock_map
585         ) 9> $BA_LOCK_PATH
586         ;;
587     scan*)
588         (
589             # lock shared
590             ba_lock_map shared || _error "could not acquire read lock" \
591                  "on $BA_LOCK_PATH"
592             ba_scandisks "$2"
593             ba_unlock_map
594         ) 9> $BA_LOCK_PATH
595         ;;
596     refresh*)
597         (
598             # lock shared
599             ba_lock_map shared || _error "could not acquire read lock" \
600                  "on $BA_LOCK_PATH"
601             ba_refresh_alias "$2"
602             ba_unlock_map
603         ) 9> $BA_LOCK_PATH
604         ;;
605     map)
606         (
607             # lock shared
608             ba_lock_map shared || _error "could not acquire read lock" \
609                 "on $BA_LOCK_PATH"
610             ba_map "$2"
611             ba_unlock_map
612         ) 9> $BA_LOCK_PATH
613         ;;
614     *)
615         ba_usage "\n{configure|createdisk|deletedisk|listdisks|scandisks|querydisk|map}"
616 esac
617
618 _STATUS="$?"
619 if [ "$_STATUS" != 0 ]; then
620     _warn "ba_main: reached end of main function with non-zero status ($?)"
621 fi
622 # catch non-zero exits
623 exit 0