:
# @(#)vdutil.sh	1.25 91/05/28
#	Copyright (C) Altos Computer Systems, 1990.
#	This Module contains Proprietary Information of
#	Altos Computer Systems and should be treated as 
#	Confidential.

# 	This is the virtual disk utility program.  Invoking this program will
# allow you to display, add, and repair a virtual disk.  
#
# IMPORTANT: This program should be used with extreme caution since it is
# possible to lose all your data on your hard disk.
#
# Completely over-hauled for use with Altos UNIX System V/386 Version 2.0
#	January 1991, Chris Benson
#

PATH=/etc:/bin:/usr/bin:/altos/bin:
export PATH

PROGNAME="$0"
fstab=/etc/default/filesys
checklist=/etc/checklist
tmp=/tmp/vd$$
VDMAJNUM=25
MAXDEV=12			# Max. number of physical devices/virtual disk
MAXVD=`vdinfo -x`		# Max. # of virtual drives, also mirrored root
ROOTMIR=`expr $MAXVD + 1`	# Minor dev number of mirror dev to root disk
FSTYP=AFS			# Floppy filesystem type
HOMEDIR=`pwd`			# Current directory
PIDFILE=/altos/bin/vddaemon.pid
SIGVDBAD=-13

# Define return values
: ${OK=0}  ${FAIL=1}  ${STOP=10}  ${HALT=11}

# FUNCTION DEFINITIONS
#########################

# Define traps for critical and non critical code.
set_trap()  {	
	trap 'echo "\n\tInterrupted! Exiting ..."; cleanup' 1 2 3 15
}
unset_trap()  {
	trap '' 1 2 3 15
}

# Remove temp files and exit with the status passed as argument
cleanup() {
	trap '' 1 2 3 15
	[ "$2" ] && delvdevs $2
	[ "$3" ] && delvdevs $3
	[ "$tmp" ] && rm -f $tmp*
	delvdevs $MAXVD
	delvdevs $ROOTMIR
	exit $1
}

# clear the screen if it is supported
clearscr() {
	# check if the terminal environment is set up
	# Take out clears so user can see previous messages, however
	# not very clean
	:
 	#[ "$TERM" ] && clear 2> /dev/null
}


# check for singleuser mode
chk_singleuser() {
	set `who -r`
	if [ ! "$3" = "S" ]
	then
		echo "
	The system must be in single-user mode to use 
	this option.
"
		sleep 3
		return 0
	fi
	return 1
}

getyn() {
	while	echo "\n\t$* (y/n): \c" >&2
	do	read yn rest
		case $yn in
		[yY])	return $OK 				;;
		[nN])	return $FAIL				;;
		*)	echo "\n\tPlease answer y or n" >&2	;;
		esac
	done
}

# Prompt with mesg, return non-zero on q. Also allows setting -x and +x,
# shell escapes and the passing of default values
prompt() {
	mesg=$1
	while	echo "\n\t${mesg}or enter 'q' to quit: \c" >&2
	do	read cmd
		case $cmd in
		+x|-x)	set $cmd					;;
		-v)	# Print the values of requested variables
			read x
			for var in $x
			do
				eval echo \$$var
			done
			;;
		Q|q)	return $FAIL					;;
		!*)	unset_trap
			eval `expr "$cmd" : "!\(.*\)"`		
			set_trap
			;;
		"")	# If there is a second argument use it
			# as the default, else loop until 'cmd' is set
			[ "$2" ] && { 
				cmd=$2
				return $OK
			}
			: continue
			;;
		*)	return $OK					;;
		esac
	done
}




#
# Remove devices.
# $1 is the virtual device number
#
delvdevs() {
    rm -f /dev/vd$1
    rm -f /dev/rvd$1
}

# Get the virtual disk number
# The first arg is the description field.  If a 2nd arg is defined check 
# if the root disk is mirrored.
get_vd() {
	desc="$1"
	ARG2="$2"
	x=`expr $MAXVD - 1`
	[ -n "$ARG2" ] && {
		set `ls -l /dev/root | sed "s/\,/ /g"`
		rootmaj="$5"
		[ "$rootmaj" = "$VDMAJNUM" ] && x=$ROOTMIR
	}
	NUM=""
	while :
	do prompt "Enter the virtual disk to $desc (0-$x)\n\t" || return $FAIL
		expr $cmd + 0 > /dev/null 2>&1	  # validity check, digits only
		if [ "$?" != "0" -a "$?" != "1" ]
		then
			echo "
	Invalid device number.  Must be between 0 and $x."
		elif [ $cmd -ge 0 -a $cmd -le $x ]
		then
			NUM=$cmd
		  	break 
		else
			echo "
	Invalid device number.  Must be between 0 and $x."
		fi
		getyn "Do you want to try again? " || return $FAIL  
	done
}



# After receiving an error on the console indicating a mismatch between
# the primary and mirror device, the user would input the
# minor device number of the primary device, the block number and
# the number of bytes so that it could be mapped on the bad track list.
# Before mapping, the disks would be read to try to extract the data if
# possible.  After mapping, the good data will be written back to the
# disk, thus restoring the mirror.
fix_badblk() {
    clearscr
    # Check if root disk is mirrored.
    set `ls -l /dev/root | sed "s/\,/ /g"`
    rootmaj="$5"
    if [ "$rootmaj" = "$VDMAJNUM" ]
    then
	x=$ROOTMIR
    else
	x=`expr $MAXVD - 1`
    fi
    while :
    do
	# Get minor device number
	while : 
	do prompt "`clearscr`
	Fix a Virtual Disk

	Enter the virtual disk number (or 'a' for all)\n\t" || return $OK
	   if [ $cmd = "a" ]
	      then
		unset_trap
		echo "\n\tFixing bad blocks...\n"
		vdbadblk -f -a 2>&1
		sigdaemon $SIGVDBAD
		echo "\n\n\tPress <Return> to continue: \c"
		read dummy
	      else
		expr $cmd + 0 > /dev/null 2>&1	  # validity check, digits only
		if [ "$?" != "0" -a "$?" != "1" ]
		then
			echo "
	Invalid device number.  Must be between 0 and $x."
			getyn "Do you want to try again? " || return $OK  
			continue
		elif [ $cmd -ge 0 -a $cmd -le $x ]
		then
			PVDNUM=$cmd

			# Check if device specified is a mirrored root disk
			if [ $PVDNUM -eq $MAXVD -o $PVDNUM -eq $ROOTMIR ]
			then
				delvdevs $MAXVD
				delvdevs $ROOTMIR
				ln /dev/root /dev/vd$MAXVD
				ln /dev/rroot /dev/rvd$MAXVD
				mknod /dev/vd$ROOTMIR b $VDMAJNUM $ROOTMIR
				mknod /dev/rvd$ROOTMIR c $VDMAJNUM $ROOTMIR
			fi
		else
			echo "
	Invalid device number.  Must be between 0 and $x."
			getyn "Do you want to try again? " || return $OK  
			continue
		fi
		grep "^$PVDNUM[ 	]" /etc/vdtab | grep M > /dev/null 2>&1
		if [ $? -ne 0 ]
		    then
			echo "\t/dev/vd$PVDNUM is not a mirrored virtual disk.
"
			getyn "Do you want to try again? " || return $OK  
			continue
		fi
		unset_trap
		echo "\n\tFixing bad blocks...\n"
		vdbadblk -f -n $PVDNUM 2>&1
		sigdaemon $SIGVDBAD
		echo "\n\n\tPress <Return> to continue: \c"
		read dummy
		if [ "$?" != "0" ]
		then
		  echo "
	Unable to fix device ($VDMAJNUM/$PVDNUM)."
			getyn "Do you wish to try again? " || return $OK
			continue
		elif [ "$MVDNUM" = "-1" ]
		then
			echo "
	$PVDNUM is not a minor device number of a mirrored disk."
			getyn "Do you wish to try again? " || return $OK
			continue
		fi
	  fi
	done
    done

    echo "\n\t$PROGNAME completed.\n"
    return $OK
}

d_menu() {
	while :
	do prompt "`clearscr`

	1. Display all virtual disks.
	2. Display a specific virtual disk.
	
	Select an option " || break
  		case $cmd in
			1) clearscr
			   vdinfo -v | more
			   echo "\n\n\tPress <Return> to continue: \c"
			   read dummy
			   return
			   ;;
			2) get_vd display || { 
				clearscr
				continue 
			   }
			   clearscr
			   vdinfo -vn $NUM 2>&1
			   echo "\n\n\tPress <Return> to continue: \c"
			   read dummy
			   return
			   ;;
			*) echo "\n\t\'$cmd\' is not a valid option. Try again."
		   	   continue  
			   ;;
		esac
	done
	return
}



a_menu() {

	while :
	do prompt "`clearscr`

	Add a Virtual Disk

	1.  Add a new virtual disk and create a file system
	2.  Add a new virtual disk WITHOUT creating a file system
	3.  Mirror the contents of an existing disk
	4.  Stripe the contents of an existing disk

	Select an option: " || break
		set_trap
	case $cmd in
		1) add.vd || exit 1
		   break
		   ;;
		2) add.vd -d || exit 1
		   break
		   ;;
		3) add.vd -m || exit 1
		   break
		   ;;
		4) add.vd -s || exit 1
		   break
		   ;;
		*) echo "\n\t\'$cmd\' is not a valid option. Try again."
		   sleep 2
		   continue  
		   ;;
	esac
	done
	return
}





b_menu() {
	clearscr
	while :
	do prompt "`clearscr`

	Display virtual disk bad block information on

	1.  all virtual disks.
	2.  a specific virtual disk.
	
	Select an option " || break
		set_trap
  		case $cmd in
			1) vdbadblk -i -a
			   echo "\n\n\tPress <Return> to continue: \c"
			   read dummy
			   ;;
			2) get_vd display || { 
				continue 
			   }
			   vdbadblk -i -n $NUM 2>&1
			   echo "\n\n\tPress <Return> to continue: \c"
			   read dummy
			   ;;
			*) echo "\n\t'$cmd\' is not a valid option. Try again."
			   sleep 2
		   	   continue  
			   ;;
		esac
	done
	return
}

clearscr() {
	[ "$TERM" ] && clear 2> /dev/null
}

sigdaemon() {

	if [ -f $PIDFILE ]
	then
	   PROCNUM=`cat $PIDFILE`
	   /bin/kill $1 $PROCNUM
	   if [ $? -ne 0 ]
	   then
		echo "\n\tError $? signalling vddaemon (pid $PROCNUM)" 
		return
	   fi
	   sleep 4
	else
	   echo "
	$PIDFILE does not exist - cannot determine process id 
	of vddaemon."
		return
	fi
}

# main()
#########################

while :
do prompt "`clearscr`

	Virtual Disk Utility Program

	This program performs maintenance tasks required to
	display disk structure, add or fix a virtual disk, or
	display virtual disk bad blocks.

	1.  Display virtual disk structure.
	2.  Add a virtual disk.
	3.  Delete a virtual disk.
	4.  Fix a virtual disk. 
	5.  Display current virtual disk bad block information.
	
	Select an option " || break
  	case $cmd in
		1) d_menu  
		   ;;

		2) chk_singleuser || a_menu
		   echo "\n\tPress <Return> to continue: \c"
		   read dummy
		   ;;

		3) chk_singleuser || del.vd
		   echo "\n\tPress <Return> to continue: \c"
		   read dummy
		   ;;

		4) chk_singleuser || fix_badblk || exit 1
		   ;;

		5) b_menu || exit 1
		   ;;

		*) echo "\n\t'$cmd' is not a valid option. Try again."
		   sleep 2
		   continue  ;;
	esac
done
exit 0
