#! /bin/sh
#
# Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#

#
# @(#)disk_clean.sh 7.8 05/04/06 SMI; TSOL 2.x

#
#  This is a clean script for removable disks
# 

# Clean scripts are called with four arguments:

# $1 is the environment in which the clean script is being run:
#    -s for standard cleanup by a user
#    -f for forced cleanup by an administrator
#    -i for boot-time initialization (when the system is booted with -r) 
#
# $2 is the device allocation name, e.g. rmdisk_0 or floppy_0.
#
# $3 is the label of the allocation, e.g. [C].  This will be the same as
#    the process label.
#
# $4 is -A if cleanup is for an allocation, or -D if cleanup is for
#    a deallocation.

# A clean script for a removable media device should prompt the user to 
# insert correctly labeled media at allocation time, and ensure that the
# media is ejected at deallocation time.

# Unless the clean script is being called for boot-time
# initialization, it may communicate with the user via stdin and
# stdout.  To communicate with the user via CDE dialogs, create a
# script or link with the same name, but with ".windowing" appended.
# For example, if the clean script specified in device_allocate is
# /etc/security/xyz_clean, that script must use stdin/stdout.  If a
# script named /etc/security/xyz_clean.windowing exists, it must use
# dialogs.  To present dialogs to the user, the dtksh script
# /etc/security/lib/wdwmsg may be used.

# This particular script, disk_clean, will work using stdin/stdout, or
# using dialogs.  A symbolic link disk_clean.windowing points to
# disk_clean.


# ####################################################
# ################  Local Functions  #################
# ####################################################

#
# Set up for windowing and non-windowing messages
#
msg_init()
{
    if [ `basename $0` != `basename $0 .windowing` ]; then
	WINDOWING="yes"
	case $VOLUME_MEDIATYPE in
	  cdrom)   TITLE="CD-ROM";;
	  rmdisk)  TITLE="Removable Disk";;
	  floppy)  TITLE="Floppy";;
	  *)       TITLE="Disk";;
	esac
	
	if [ "$MODE" = "allocate" ]; then
	    TITLE="$TITLE Allocation"
	else
	    TITLE="$TITLE Deallocation"
	fi
    else
	WINDOWING="no"
    fi
}

#
# Display a message for the user.  For windowing, user must press OK button 
# to continue. For non-windowing, no response is required.
#
msg() {
    if [ "$WINDOWING" = "yes" ]; then
	$WDWMSG "$*" "$TITLE" OK
    else  
	echo "$*" > /dev/tty
    fi
}

#
# Ask the user an OK/Cancel question.  Return 0 for OK, 1 for Cancel.
#
okcancel() {
    if [ "$WINDOWING" = "yes" ]; then
	$WDWMSG "$*" "$TITLE" OK Cancel
    else  
	get_reply "$* (y to continue, n to cancel) \c" y n
    fi
}

#
# Ask the user an Yes/No question.  Return 0 for Yes, 1 for No
#
yesno() {
    if [ "$WINDOWING" = "yes" ]; then
	$WDWMSG "$*" "$TITLE" Yes No
    else  
	get_reply "$* (y/n) \c" y n
    fi
}

#
# Display an error message, put the device in the error state, and exit.
#
error_exit() {
    chown bin $VOLUME_PATH
    chmod 0100 $VOLUME_PATH
    msg "$2" "$3" "\n\nDevice has been placed in allocation error state." \
	"\nPlease inform system administrator."
    exit 1
}

#
# get_reply prompt choice ...
#
get_reply() {
    prompt=$1; shift
    while true
    do
	echo $prompt > /dev/tty
	read reply
	i=0
	for choice in $*
	do
	    if [ "$choice" = "$reply" ]
	    then
		return $i
	    else
		i=`expr $i + 1`
	    fi
	done
    done
}

#
# Find the first disk slice containing a HSFS file system
#
find_fs()
{
    for DEVn in $FILES ; do
      x="`labelit -F hsfs $DEVn 2>&1| grep 'Volume id'`"
      if [ $? = 0 ]; then
	  FSPATH=$DEVn
	if [ "$x" != "" ]; then
	  y="`echo $x|cut -f3- -d' '|/usr/xpg4/bin/tr '[:upper:] ' '[:lower:]_'`"
	  FSNAME=$y
	fi
	return
      fi
    done
}

#
# Find all mountpoints in use for a set of device special files.
# Usage: findmounts devpath ...
#

findmounts() {
  nawk -f - -v vold_root="$VOLD_ROOT" -v devs="$*" /etc/mnttab <<"ENDOFAWKPGM"
	BEGIN {
		split(devs, devlist, " ");
		for (devN in devlist) {
			dev = devlist[devN];
			realdevlist[dev] = 1;
			sub(/.*\//, "", dev);
			sub(/s[0-9]$/, "", dev);
			if (vold_root != "") {
				vold_dir[vold_root "/dev/dsk/" dev] = 1;
				vold_dir[vold_root "/dev/rdsk/" dev] = 1;
			}
		}
	}

	{
		for (dev in realdevlist) {
			if ($1 == dev) {
				mountpoint = $2;
				print mountpoint;
			}
		}
		for (dev in vold_dir) {
			if (substr($1, 1, length(dev)) == dev) {
				mountpoint = $2;
				print mountpoint;
			}
		}
	}
ENDOFAWKPGM
}


#
# Allocate a device.
# Ask the user to make sure the disk is properly labeled.
# Ask if the disk should be mounted.
#
do_allocate()
{
    if [ $VOLUME_MEDIATYPE = floppy ]; then
	eject_msg="`eject -q $DEVFILE 2>&1`" # Determine if media is in drive
	eject_status="$?"
	case $eject_status in
	  0)			# Media is in drive
	     yesno "Is disk in $DEVICE labeled $LABEL?"
	     if [ $? != 0 ]; then
		 exit 1
	     fi;;
	  1)			# Media is not in drive
	     okcancel "Insert disk labeled $LABEL in $DEVICE."
	     if [ $? != 0 ]; then
		 exit 1
	     fi;;
	  3)			# Error 
	     error_exit $DEVICE "Error checking for media in drive.";;
	esac
	
    else
	okcancel "Insert disk labeled $LABEL in $DEVICE."
	if [ $? != 0 ]; then
	    exit 1
	fi
    fi
    
    yesno "Do you want $DEVICE mounted?"
    if [ $? != 0 ]; then
	exit 0
    fi

    if [ $VOLUME_MEDIATYPE = cdrom -o $VOLUME_MEDIATYPE = rmdisk ]; then
	# Get the device path and volume name of a partition
	find_fs
	if [ "$FSPATH" != "" ]; then
	    VOLUME_PATH=$FSPATH	
	fi
	if [ "$FSNAME" != "" ]; then
	    VOLUME_NAME=$USERDIR/$FSNAME
	    VOLNAME=$FSNAME
	fi
    fi
    VOLUME_ACTION=insert

    # Give ourself write permission on device file so file system gets
    # mounted read/write if possible.  Set the label to specify label 
    # for the file system when it's mounted.  These same attributes will
    # get set by allocate, but we set them temporarily during the mount.
    chown $VOLUME_USER $VOLUME_PATH
    chmod 700 $VOLUME_PATH
    setlabel "$LABEL" $VOLUME_PATH

    # Delete old mount point directories
    rmdir /$VOLUME_MEDIATYPE/$USERDIR/* 2>/dev/null
    rmdir_msg=`rmdir /$VOLUME_MEDIATYPE/$USERDIR 2>&1`
    if [ -d /$VOLUME_MEDIATYPE/$USERDIR ]; then
	error_exit $DEVICE \
	    "\nUnable to clean up directory /$VOLUME_MEDIATYPE/$USERDIR" \
	    "\n$rmdir_msg"
    fi

    # Create new mount point directories
    mkdir -p /$VOLUME_MEDIATYPE/$USERDIR/$VOLNAME
    chown $VOLUME_USER /$VOLUME_MEDIATYPE/$USERDIR/$VOLNAME

    # Set attributes of /<mediatype>
    chmod 777 /$VOLUME_MEDIATYPE
    setlabel -s "admin_low" /$VOLUME_MEDIATYPE	

    # Set attributes of /<mediatype>/<user>
    chown $VOLUME_USER /$VOLUME_MEDIATYPE/$USERDIR
    chmod 700 /$VOLUME_MEDIATYPE/$USERDIR
    setlabel -s "$LABEL" /$VOLUME_MEDIATYPE/$USERDIR

    # Give ourself write permission on device file so file system gets
    # mounted read/write if possible.  Set the label to specify label 
    # for the file system when it's mounted.  These same security
    # attributes will get set by allocate, but we need to set them 
    # temporarily during the mount.
    chown $VOLUME_USER $VOLUME_PATH
    chmod 700 $VOLUME_PATH
    setlabel "$LABEL" $VOLUME_PATH

    # Do the actual mount.  VOLUME_* environment variables
    # are inputs to rmmount.
    /usr/sbin/rmmount

    # Reset the access on the device file, in case allocation fails
    # for any reason.  allocate will do final setting of the access.
    chown root $VOLUME_PATH
    chmod 000 $VOLUME_PATH
    setlabel -s admin_low $VOLUME_PATH

    # Set permissions on directory used by vold, sdtvolcheck, etc.
    if [ -d /tmp/.removable ]; then
	chown root /tmp/.removable
	chmod 777 /tmp/.removable
    fi

    # Add a link in /$VOLUME_MEDIATYPE, so /$VOLUME_MEDIATYPE/$VOLNAME
    # refers to the mount points, as it does in standard Solaris.
    (
    cd /$VOLUME_MEDIATYPE
    rm -f $VOLNAME
    ln -s ./$USERDIR/$VOLNAME $VOLNAME
    )

}


do_deallocate()
{
    if [ $VOLUME_MEDIATYPE = cdrom -o $VOLUME_MEDIATYPE = rmdisk ]; then
	# Get the device path and volume name of a partition
	find_fs
	if [ "$FSPATH" != "" ]; then
	    VOLUME_PATH=$FSPATH	
	fi
	if [ "$FSNAME" != "" ]; then
	    VOLUME_NAME=$USERDIR/$FSNAME
	    VOLNAME=$FSNAME
	fi
    fi
    VOLUME_ACTION=eject

    # Do the actual unmount.  VOLUME_* environment variables
    # are inputs to rmmount.
    rmmount_msg="`/usr/sbin/rmmount 2>&1`"
    rmmount_status="$?"

    # Remove symbolic links to mount point
    for name in /$VOLUME_MEDIATYPE/*; do
	if [ -h $name ]; then
	    target=`ls -l $name | awk '{ print $NF; }'`
	    target_dir=`dirname $target`
	    target_device=`echo $target_dir | sed -e 's/^.*-\(.*\)$/\1/'`
	    if [ "$target_device" = "$DEVICE" ]; then
		rm -f $name
	    fi
	fi
    done

    case $rmmount_status in
      1)		# still mounted
	 error_exit $DEVICE "Error unmounting $DEVICE" "$rmmount_msg";;
      0)		# not mounted
	 # Remove the mountpoint and the user directory above it
	 if [ -d /$VOLUME_NAME ]; then
	     rm_msg=`rmdir /$VOLUME_NAME`
	     rm_status="$?"
	 else
	     rm_status=0 
	 fi
	 if [ $rm_status = 0 -a -d /$VOLUME_MEDIATYPE/$USERDIR ]; then
	     rm_msg=`rm -rf /$VOLUME_MEDIATYPE/$USERDIR 2>&1`
	     rm_status="$?"
	 else
	     rm_status=0 
	 fi

	 # Eject the media
	 if [ "$FLAG" = "f" ] ; then
	     eject_msg="`eject -f $DEVFILE 2>&1`"
	 else
	     eject_msg="`eject $DEVFILE 2>&1`"
	 fi
	 eject_status="$?"
	 case $eject_status in
	   0)				# Media has been ejected
	      case $VOLUME_MEDIATYPE in
		floppy)
			msg "Please remove the disk from $DEVICE" \
			    "\nand verify it is labeled $LABEL." ;;
		cdrom|rmdisk)
		       msg "Please remove the disk from $DEVICE." ;;
	      esac;;
	   1|3)				# Media didn't eject
		error_exit $DEVICE "Error ejecting disk from $DEVICE" \
		    "$eject_msg";;
	 esac

	 if [ $rm_status != 0 ]; then
	     error_exit $DEVICE "Unable to delete directory" \
		 "/$VOLUME_MEDIATYPE/$USERDIR" \
		 "\n$rm_msg"
	 fi;;
    esac
}

#
# Reclaim a device
#
do_init()
{
    eject_msg="`eject -f $DEVFILE 2>&1`"
    eject_status="$?"

    case $eject_status in
      0)				# Media has been ejected 
	 echo "Operator must label disk from" \
	     "$DEVICE with $LABEL" > /dev/console
	 exit 0;;
      1)				# Media not ejected
	 exit 0;;
      3)			# Error 
	 echo "Error cleaning up $DEVICE," \
	     "label: $LABEL" > /dev/console
	 error_exit $DEVICE "Error ejecting disk from $DEVICE" "$eject_msg"
    esac
}


# ####################################################
# ################ Begin main program ################
# ####################################################

trap "" INT TERM QUIT TSTP ABRT

PATH="/usr/bin:/usr/sbin"
ALLOCATE="-A"
WDWMSG="/etc/security/lib/wdwmsg"

#
# Parse the command line arguments
#
while getopts ifs c
  do
      case $c in
	i)   FLAG=$c;;
	f)   FLAG=$c;;
	s)   FLAG=$c;;
	\?)   echo "Usage: disk_clean [-s|-f|-i] device cmw_label mode"
	    exit 1;;
      esac
done
shift `expr $OPTIND - 1`

DEVICE=$1
LABEL=$2
if [ "$3" = "-A" ]; then
      MODE="allocate"
fi


# get the device_maps information

MAP=`dminfo -v -n $DEVICE`
FILES=`echo $MAP | cut -f3 -d:`	# e.g., /dev/dsk/c0t6d0s0 /dev/dsk/c0t6d0s1 ...
DEVFILE=`echo $FILES | cut -f1 -d" "` 		# e.g., "/dev/dsk/c0t6d0s0"

# Set VOLUME_ variables that are inputs to rmmount

# Note that VOLUME_NAME is set to <username>-<device>/<volname>.  rmmount
# may not really expect a volume name that includes a slash, but it
# works fine.

VOLUME_DEVICE=`echo $FILES | cut -f2 -d" "` 	# e.g., "/dev/dsk/c0t6d0s1"
VOLUME_MEDIATYPE=`echo $MAP | cut -f1 -d_` 	# e.g., "cdrom" or "floppy"
VOLUME_PATH=$DEVFILE				# e.g., "/dev/dsk/c0t6d0s0"
VOLUME_SYMDEV=`echo $DEVICE | sed -e 's/_//'`	# e.g., "cdrom0" or "floppy0"
if [ "$MODE" = "allocate" ]; then
    VOLUME_USER=`/usr/xpg4/bin/id -u -nr`	# e.g., "jhoman"
else
    # If there's a directory for the device under /<mediatype>, get the 
    # user name from there, to use in cleaning up that directory.  Otherwise,
    # the user name isn't actually used in deallocation.
    if [ -d /$VOLUME_MEDIATYPE/*-$DEVICE ]; then
	VOLUME_USER=`ls -ld /$VOLUME_MEDIATYPE/*-$DEVICE | \
	 awk '/^d/{print $3}'`
    else
	VOLUME_USER=nobody
    fi
fi    
VOLUME_NAME=$VOLUME_USER-$DEVICE/unnamed_$VOLUME_MEDIATYPE
				# e.g., "jhoman-cdrom_0/unnamed_cdrom"

export VOLUME_ACTION VOLUME_DEVICE VOLUME_MEDIATYPE VOLUME_NAME
export VOLUME_PATH VOLUME_SYMDEV VOLUME_USER

USERDIR=$VOLUME_USER-$DEVICE			# e.g., "jhoman-cdrom_0"
VOLNAME=unnamed_$VOLUME_MEDIATYPE 		# e.g., "unnamed_cdrom"

msg_init

if [ "$MODE" = "allocate" ]; then
  	do_allocate
else
    if [ "$FLAG" = "i" ] ; then
	do_init
    else
	do_deallocate
    fi
fi

exit 0
