Make local variable usage consistent throughout
[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
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 declare -A WWIDMAP
24 declare -A NAMEMAP
25 declare -A TYPEMAP
26 declare -A PARTMAP
27
28 # To enable debug output set the BA_DEBUG environment variable to "yes".
29 #BA_DEBUG="yes"
30 BA_DEFAULT_USER="oracle"
31 BA_DEFAULT_GROUP="oracle"
32 BA_CONF_PATH="/etc/blkdevalias"
33 BA_DEV_PATH="oracleasm/disks"
34 BA_MAP_PATH="${BA_CONF_PATH}/blkdevalias.map"
35 BA_CONF="${BA_CONF_PATH}/blkdevalias.conf"
36 BA_USER="$BA_DEFAULT_USER"
37 BA_GROUP="$BA_DEFAULT_GROUP"
38 BA_LOCK="/var/lock/blkdevalias"
39 BA_SCSI_ID="/sbin/scsi_id"
40 BA_SCSI_ID_FLAGS="-g -u"
41 BA_READLINK="/usr/bin/readlink"
42 BA_BASENAME="/bin/basename"
43 BA_GETENT="/usr/bin/getent"
44 BA_CHOWN="/bin/chown"
45 BA_SUDO="/usr/bin/sudo"
46 BA_SED="/bin/sed"
47 BA_ID="/usr/bin/id"
48 BA_SH="/bin/bash"
49 BA_REAL_UID="$($BA_ID -u)"
50 BA_REAL_GID="$($BA_ID -g)"
51
52 # load config
53 . $BA_CONF
54
55 _error () {
56     echo -e "ERROR: $@" 1>&2
57 }
58
59 _warn () {
60     echo -e "WARNING: $@" 1>&2
61 }
62
63 _debug () {
64     [ "$BA_DEBUG" == "yes" ] && echo -e "$@" 1>&2
65 }
66
67 # store the WWID map to disk
68 ba_store_wwid_map() {
69     local _WWID _TMP_MAP
70     _TMP_MAP=$(mktemp --tmpdir=$BA_CONF_DIR)
71     if [ ! -f "$_TMP_MAP" ]; then
72         _error "could not write temporary file"
73         exit 1
74     fi
75     for _WWID in ${!WWIDMAP[@]}; do
76         echo "$_WWID ${WWIDMAP[$_WWID]} ${TYPEMAP[$_WWID]} ${PARTMAP[$_WWID]}"
77     done  >> $_TMP_MAP
78     mv $_TMP_MAP $BA_MAP_PATH
79     # we may be running as root - if so re-chown the configs
80     if [ "$BA_REAL_UID" == "0" ] || [ "$BA_REAL_GID" == "0" ]; then
81         ba_chown_configs $BA_USER $BA_GROUP
82     fi
83 }
84
85 # read the WWID map from disk
86 ba_load_wwid_map() {
87     local _WWID _ALIAS _TYPE _PART
88     if [ ! -f "$BA_MAP_PATH" ] || [ ! -r "$BA_MAP_PATH"  ]; then
89         _error "$BA_MAP_PATH does not exist or is not readable"
90         exit 1
91     fi
92     while read _WWID _ALIAS _TYPE _PART; do
93         if [[ $_WWID == \#* ]] || [[ $_WWID == "" ]]; then
94             continue
95         fi
96         WWIDMAP[$_WWID]="$_ALIAS"
97         NAMEMAP[$_ALIAS]="$_WWID"
98         TYPEMAP[$_WWID]="$_TYPE"
99         PARTMAP[$_WWID]="$_PART"
100     done < $BA_MAP_PATH
101 }
102
103 # ba_add_wwid_mapping <wwid> <alias> <sd|mpath> <part>
104 ba_add_wwid_mapping() {
105     local _WWID _ALIAS _TYPE _PART _DEV
106     _WWID="$1"
107     _ALIAS="$2"
108     _TYPE="$3"
109     _PART="$4"
110     _DEV="$5"
111     _debug "ba_add_wwid_mapping: WWID=\"$_WWID\" ALIAS=\"$_ALIAS\"" \
112         "TYPE=\"$_TYPE\" PART=\"$_PART\""
113     if [ "$_WWID" == "" ]; then
114         _warn "no wwid given"
115         return
116     fi
117     if [ "$_ALIAS" == "" ]; then
118         _warn "no alias given"
119         return
120     fi
121     if [ "$_PART" == "" ]; then
122         _warn "no partition number given"
123     fi
124     if [ "${WWIDMAP[$_WWID]}" != "" ]; then
125         _debug "ba_add_wwid_mapping: WWIDMAP[\$WWID]=${WWIDMAP[$_WWID]}"
126         _warn "$DEV already mapped to alias ${WWIDMAP[$_WWID]}; replacing it."
127         ba_del_wwid_mapping "$_WWID"
128     fi
129     if [ "${NAMEMAP[$_ALIAS]}" != "" ]; then
130         _debug "ba_add_wwid_mapping: NAMEMAP[\$ALIAS]=${NAMEMAP[$_ALIAS]}"
131         _warn "$DEV already mapped to disk ${NAMEMAP[$_ALIAS]}; replacing it."
132         ba_del_wwid_mapping ${NAMEMAP[$_ALIAS]}
133     fi
134     WWIDMAP[$_WWID]="$_ALIAS"
135     NAMEMAP[$_ALIAS]="$_WWID"
136     TYPEMAP[$_WWID]=${_TYPE:-"sd"}
137     PARTMAP[$_WWID]="$_PART"
138 }
139
140 # ba_del_wwid_mapping <wwid>
141 ba_del_wwid_mapping() {
142     local _WWID _ALIAS
143     _WWID="$1"
144     if [ "$_WWID" == "" ]; then
145         return 1
146     fi
147     if [ "${WWIDMAP[$_WWID]}" == "" ]; then
148         _warn "WARNING: $_WWID does not exist; doing nothing."
149         return 1
150     fi
151     _debug "\nba_del_wwid_mapping: unsetting WWIDMAP[$_WWID]"
152     _ALIAS=${WWIDMAP[$_WWID]}
153     unset WWIDMAP[$_WWID]
154     unset NAMEMAP[$_ALIAS]
155     unset TYPEMAP[$_WWID]
156     unset PARTMAP[$_WWID]
157 }
158
159 ba_get_partnum () {
160     local _DEV _NUM
161     _DEV="$1"
162     if [[ $DEV == dm-* ]]; then
163         _DEV=$DM_NAME
164     fi
165     _debug "ba_get_partnum: $_DEV"
166     /sbin/dmsetup info $_DEV > /dev/null 2>&1
167     if [ "$?" == "0" ]; then
168         _NUM=$(echo $_DEV | sed -e 's/\/dev.*\///' -e 's/.*p\([0-9]*\).*/\1/')
169     else
170         _NUM=$(echo $_DEV | sed 's/\(\/dev\/\)*sd[a-z]*//')
171     fi
172     if [ "$_NUM" == "" ]; then
173         _NUM="0"
174     fi
175     echo $_NUM
176 }
177
178 ba_scsi_id () {
179     local _DEV
180     _DEV="$1"
181     _debug "ba_scsi_id: getting ID for device $_DEV"
182     dmsetup info $_DEV > /dev/null  2>&1
183     if [ $? -eq 0 ]; then
184         _DEV=$(echo $_DEV | sed 's/p[0-9].*//')
185     else
186         _DEV=$(echo $_DEV | sed 's/[0-9].*//')
187     fi
188      _debug "ba_scsi_id: calling \"$BA_SCSI_ID $BA_SCSI_ID_FLAGS $_DEV\""
189     $BA_SUDO $BA_SCSI_ID $BA_SCSI_ID_FLAGS $_DEV
190 }
191
192 # ba_chown_configs <user> <group>
193 ba_chown_configs () {
194     local _USER _GROUP _OUT
195     _USER="$1"
196     _GROUP="$2"
197     $BA_CHOWN -R "$_USER:$_GROUP" "$BA_CONF_PATH"
198     ls -l "$BA_CONF_PATH" | while read _OUT; do
199         _debug $_OUT
200     done
201     return 0
202 }
203
204 # ba_refresh_device <dev path>
205 ba_refresh_device () {
206     local _DEVPATH _DEVNAME _LINKDEST
207     _DEVPATH="$1"
208     _debug "refreshing device at path $_DEVPATH"
209     _LINKDEST=$($BA_READLINK $_DEVPATH)
210     if [ "$?" != 0 ]; then
211         _DEVNAME=$($BA_BASENAME $_DEVPATH | $BA_SED 's/p\+[1-9]*$//')
212         _debug "non-symlinked dev at $_DEVPATH has name $_DEVNAME"
213     else
214         _DEVNAME=$($BA_BASENAME $_LINKDEST)
215         _debug "symlinked dev at $_DEVPATH has name $_DEVNAME"
216     fi
217     _debug "requesting change uevent at sysfs path /sys/block/$_DEVNAME"
218     $BA_SUDO $BA_SH -c "echo change > /sys/block/$_DEVNAME/uevent"
219 }
220
221 ba_configure () {
222     local _BA_USER _BA_GROUP _BA_DEV_PATH _TMP_CONF
223     cat <<EOF
224 Configuring the blkdevalias map.
225
226 This will configure persistent aliases and permissions for storage
227 devices. The current values will be shown in brackets ('[]'). Hitting
228 <ENTER> without typing an answer will keep the current value.
229
230 Ctrl-C will abort.
231
232 EOF
233
234     echo -n "Default user to own device nodes [$BA_USER]: "
235     read _BA_USER
236     if [ "$_BA_USER" != "" ]; then
237         BA_USER=$_BA_USER;
238     fi
239     $BA_GETENT passwd "$BA_USER" &>/dev/null
240     if [ "$?" != 0 ]; then
241         _error "user (\"$BA_USER\") must be a valid user account"
242         exit 1
243     fi
244
245     echo -n "Default group to own device nodes [$BA_GROUP]: "
246     read _BA_GROUP
247     if [ "$_BA_GROUP" != "" ]; then
248         BA_GROUP=$_BA_GROUP;
249     fi
250     $BA_GETENT group "$BA_GROUP" &>/dev/null
251     if [ "$?" != 0 ]; then
252         _error "user (\"$BA_GROUP\") must be a valid group account"
253         exit 1
254     fi
255     echo -n "Default device directory [$BA_DEV_PATH]: "
256     read _BA_DEV_PATH
257     if [ "$_BA_DEV_PATH" != "" ]; then
258         BA_DEV_PATH=$_BA_DEV_PATH;
259     fi
260
261     _debug "BA_DEV_PATH=\"$BA_DEV_PATH\""
262     _debug "BA_USER=\"$BA_USER\""
263     _debug "BA_GROUP=\"$BA_GROUP\""
264     echo -n "Writing wwid map configuration: "
265     TMP_CONF=$(mktemp --tmpdir=$BA_CONF_PATH)
266     if [ ! -f "$TMP_CONF" ]; then
267         echo
268         _error "could not write temporary file"
269         exit 1
270     fi
271     echo "BA_DEV_PATH=\"$BA_DEV_PATH\"" >> $TMP_CONF
272     echo "BA_USER=\"$BA_USER\"" >> $TMP_CONF
273     echo "BA_GROUP=\"$BA_GROUP\"" >> $TMP_CONF
274     mv $TMP_CONF $BA_CONF
275     if [ "$?" != 0 ]; then
276         _error "could not create configuration file $BA_CONF"
277         exit
278     fi
279     # set configration ownership to reflect admin users
280     ba_chown_configs "$BA_USER" "$BA_GROUP"
281     echo -e "done\n"
282 }
283
284 # createdisk <alias> <device>
285 ba_createdisk () {
286     local _WWID _DEV _ALIAS _TYPE _PART
287     _ALIAS="$1"
288     _DEV="$2"
289     if [ "$_DEV" == "" ] || [ "$_ALIAS" == "" ]; then
290         echo "Usage: $0 createdisk <alias> <device>"
291         exit 2
292     fi
293     _debug "ba_createdisk: DEV=\"$_DEV\" ALIAS=\"$_ALIAS\""
294
295     _WWID=$(ba_scsi_id $_DEV)
296     if [ "$_WWID" == "" ]; then
297         _error "could not get SCSI ID for $_DEV"
298         exit 1
299     fi
300     _debug "ba_createdisk: WWID=\"$_WWID\""
301
302     if dmsetup info $_DEV > /dev/null  2>&1; then
303         _TYPE="mpath"
304     else
305         _TYPE="sd"
306     fi
307
308     _PART=$(ba_get_partnum $_DEV) 
309     _debug "ba_createdisk: PART=\"$_PART\""
310
311     ba_add_wwid_mapping $_WWID $_ALIAS $_TYPE $_PART $_DEV
312     ba_store_wwid_map
313     ba_refresh_device $_DEV
314 }
315
316 ba_deletedisk () {
317     local _WWID _ALIAS _DEVPATH
318     _ALIAS=$1
319     if [ "$_ALIAS" == "" ]; then
320         echo "Usage: $0 deletedisk <alias>"
321         exit 2
322     fi
323     _WWID=${NAMEMAP[$_ALIAS]}
324     if [ "$_WWID" == "" ]; then
325         _error "\"$_ALIAS\" does not exist"
326         echo "Usage: $0 deletedisk <alias>"
327         exit 2
328     fi
329     echo -n "Removing wwid map disk \"$_ALIAS\":"
330     # generate device path for refresh call
331     _DEVPATH="/dev/$BA_DEV_PATH/${WWIDMAP[$_WWID]}"
332     ba_del_wwid_mapping $_WWID \
333     && if [ "$BA_DEBUG" != "yes" ]; then
334         echo "                                  [  OK  ]"
335     fi
336     ba_store_wwid_map
337     ba_refresh_device $_DEVPATH
338 }
339
340 ba_list_disks () {
341     local _D
342     for _D in ${WWIDMAP[@]}; do
343         local _DEVPATH
344         _DEVPATH="/dev/$BA_DEV_PATH/${WWIDMAP[${NAMEMAP[$_D]}]}"
345         echo -n $_D " $_DEVPATH -> "
346         echo $(readlink $_DEVPATH)
347     done | sort -V
348 }
349
350 ba_querydisk () {
351     local _LISTDEV _ONDEV _WWID _ALIAS
352     if [ "$1" == "-d" ]; then
353         _LISTDEV=1
354         shift
355     fi
356     _ALIAS=$1
357     if [ "$_ALIAS" == "" ]; then
358         echo "Usage: $0 querydisk <alias>"
359         exit 2
360     fi
361     _WWID=${NAMEMAP[$_ALIAS]}
362     _debug "ba_querydisk: ALIAS=\"$_ALIAS\" WWID=\"$_WWID\" LISTDEV=\"$_LISTDEV\""
363     if [ "$_LISTDEV" == "1" ]; then
364         DEV="/dev/$BA_DEV_PATH/$(readlink "/dev/$BA_DEV_PATH/$_ALIAS")"
365         MAJ="$[0x$(stat --format "%t" $DEV)]"
366         MIN="$[0x$(stat --format "%T" $DEV)]"
367         _ONDEV="on device [$MAJ,$MIN]"
368         _debug "ba_querydisk: DEV=\"$DEV\" MAJ=\"$MAJ\" MIN=\"$MIN\""
369     fi
370     if [ "$_WWID" != "" ]; then
371         echo "$_ALIAS is a valid blkdevalias disk $_ONDEV"
372         return
373     fi
374     exit 1
375 }
376
377 ba_scandisks () {
378     _warn "(unimplemented)"
379 }
380
381 ba_map () {
382     local _WWID _TYPE _PART
383     _WWID="$1"
384     if [ "$_WWID" == "" ] || [ "${WWIDMAP[$_WWID]}" == "" ]; then
385         echo "Usage: $0 map <wwid>" 1>&2
386         exit 2
387     fi
388     _PART=$(ba_get_partnum $DEVNAME)
389     if [ "$ID_BUS" == "scsi" ]; then
390         _TYPE=sd
391     else
392         _TYPE=mpath
393     fi
394     if [ "$_PART" != ${PARTMAP[$_WWID]} ] \
395         || [ "$_TYPE" != ${TYPEMAP[$_WWID]} ] ; then
396         exit 1
397     fi
398     echo "BA_WWID=$_WWID"
399     echo "BA_NAME=$BA_DEV_PATH/${WWIDMAP[$_WWID]}"
400     echo "BA_TYPE=${TYPEMAP[$_WWID]}"
401     echo "BA_USER=$BA_USER"
402     echo "BA_GROUP=$BA_GROUP"
403     echo "BA_MODE=$BA_MODE"
404 }
405
406 # no point optimizing this out for configure
407 ba_load_wwid_map
408 case "$1" in
409     configure)
410         ba_configure
411         ;;
412     create*)
413         ba_createdisk $2 $3
414         ;;
415     delete*)
416         ba_deletedisk $2
417         ;;
418     list*)
419         ba_list_disks
420         ;;
421     query*)
422         ba_querydisk $2 $3
423         ;;
424     scan*)
425         ba_scandisks
426         ;;
427     map)
428         ba_map $2
429         ;;
430     *)
431         echo -n "Usage: $0 "
432         echo "{configure|createdisk|deletedisk|listdisks|scandisks|querydisk|map}"
433         exit 2
434 esac
435