#!/bin/ksh
#
#ident	"@(#)config.s+h	1.10	04/12/08 SMI"
#
# Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#
# config.s+h - configure a system for running storage tests included in HCTS.
#
# usage: config.s+h [-d][-v][-n]
#
# where:  -d  enables debug mode
# where:  -v  enables verbose mode
# where:  -n  continue without prompting the users
#
# This script configures a system for running storage tests included in HCTS.
# To use it, there must be at least one disk in the system that
# does not contain system files.  It needs to have an fdisk table,
# a Solaris partition, and existing device nodes in /dev.  There
# is no limit to the number of disks that can be used for testing.
# Configuration is done according to information supplied to this
# program by the user-created ./config.s+h.info file (the "info-file").
# This file has entries in the following format:
#
# device [slices] [type] [mount]
#
# where device  is the device name, in cNtNdN or cNdN format;
#       slices  is the number of slices to create on the disk (this
#                  is optional and the default is MAXSLICE, which is
#                  currently 15);
#       type    is the type of slices to create (raw or filesystems)
#                  (this is optional and the default is raw);
#       mount   is the mount-point prefix for filesystems (this
#                  is optional and the default is MOUNT, which is
#                  currently /slice).
#
# To illustrate use of the info-file, here are some sample entries
# with explantations of what they do:
#
# c0t1d0 8 f test      # Put eight (8) slices on /dev/rdsk/c0t1d0.
#                      # Build filesystems on all eight slices.
#                      # Mount the filesystems on /test.c0t1d0s1
#                      # through /test.c0t1d0s8.  Create /etc/vfstab
#                      # entries for all filesystems.
#
# c0d0 10 r            # Put ten (10) slices on /dev/rdsk/c0d0.
#                      # Use all of them as raw devices.
#
# c2t0d0 6 fr tst      # Put six (6) slices on /dev/rdsk/c2t0d0.
#                      # Build a filesystem on slice 1, use slice 2 as
#                      # a raw device, build a filesystem on slice 3,
#                      # use slice 4 as a raw device, etc. Mount the
#                      # three filesystems on /tst.c2t0d0s1,
#                      # /tst.c2t0d0s3 and /tst.c2t0d0s5.
#
# c2t0d0 6 er tst      # Same as above, but export the filesystems
#                      # and create /etc/dfs/dfstab entries for them.
#
# c2t0d0               # Put MAXSLICE (15) slices on /dev/rdsk/c2t0d0.
#                      # Use all of them as raw devices.
#
# c2t0d0 e             # Put MAXSLICE (15) slices on /dev/rdsk/c2t0d0.
#                      # Build filesystems on all slices.  Mount the
#                      # filesystems on /slice.c2t0d0s0 through
#                      # /slice.c2t0d0s14.  Create /etc/vfstab entries
#                      # for all filesystems.  Export all filesystems
#                      # and create /etc/dfs/dfstab entries for them.
#
# Slices are numbered starting at zero (0).  All slices start on cylinder
# boundaries and are relatively equal in size, with any slight residual
# going into the last slice.  The highest-numbered slice gets the first
# cylinder, which holds the VTOC.  Slice 0 begins on the fourth cylinder.
# There is a maximum limit of sixteen (16) physical slices; with the last
# slice reserved for the VTOC, a maximum limit of fifteen (15) slices
# can be used for testing.  Slice two (2) does not represent the entire
# disk, and can be treated like any other slice, which it is.  Entries
# for each filesystem are made in /etc/vfstab. Finally, the optimum value
# of MAX_BLOCKS is determined and stored in the file "env.s+h". The RUN
# script has been modified to read and use this value of MAX_BLOCKS.
#
# This program will not slice any disks which contain the /, /usr, /opt,
# /var, or /export/home* filesystems and, if present, entries in the
# info-file asking for such action are removed.  In addition, entries
# that are malformed or incomplete are also removed, with the following
# exceptions: 1) slice type defaults to raw in the event that the type
# filed is missing or incorrect; 2) mount point defaults to MOUNT in
# the event that the mount field is missing; and 3) the slice field is
# reset to MAXSLICE in the event that it is greater than MAXSLICE.
#
# NOTE that this program reads the info-file twice.  On the first pass,
# extensive error checking is done, and the info-file may be rewritten
# to obviate need to repeat these error checks on the second pass.
#
# NOTE that you must be root to run this script.
#
# ---------------------------------------------------------------------
#
# Revision History
#
# 1.0  09/16/98  Wendell Holmes  Initial draft.
# 2.0  10/13/98  Wendell Holmes  Modified to use info file.
# 2.3  03/04/99  Wendell Holmes  Fix bug in MAX_BLOCKS calculation;
#                                Add -d (debug) command-line option;
#                                Add Revision History.
#
# =====================================================================

CF="config.s+h.info"  # configuration information file
MAXSLICE=15           # maximim user slices
MOUNT=slice           # default filesystem mount point

integer debug
integer verbose
integer max_sec
integer max_slice
integer i
integer cnt
integer flag
integer slice
integer fs
integer raw
integer min_avail
integer export
integer daemons

DIFS=$IFS  # save default IFS
debug=0
verbose=0
prompt=0
slice_flag=0


# Cleanup old files

cleanup() {

[ -f reserved.* ] && rm reserved.*
[ -f vtoc.*  ]    && rm vtoc.*
[ -f fs.* ]       && rm fs.*
[ -f size.slice ] && rm size.slice
[ -f tmp.* ]      && rm tmp.*
}

cleanup

[ -f targets ] && rm targets
[ -f cleanup ] && rm cleanup

[ -f $CF ] || { echo "$0: missing $CF file"; exit 0; }

# Process command-line arguments

if [ "$#" -gt 0 ]
then
    for arg in $*
    do
        case "$arg"
        in
            "-d" ) debug=1;;
            "-v" ) verbose=1;;
            "-n" ) prompt=1;;
        esac
    done

fi

# Determine which drives we won't touch.  These are the drives containing
# /, /usr, /opt, /var and /export/home*.  Convert the pathnames to the
# device names (cNtNdN or cNdN).

[ "$verbose" -eq 1 ] && echo "Determining which drives to reserve"

df -k | egrep ' /$| /usr$| /opt$| /var$| /export/home*' | cut "-d " -f 1 |\
sed -e 's;\/dev\/dsk\/;;'\
    -e 's;s.$;;' |\
sort | uniq > reserved.$$
reserved=`cat reserved.$$`  # Load device names in string for faster parsing

# Read the configuration file and create VTOCs for use by fmthard.
# Make sure $CF is properly constructed, rebuilding it if necessary.

IFS="
"
for line in `cat $CF`
do
    IFS=$DIFS
    dev=${line%% *}
    rpath="/dev/rdsk/${dev}s2"
    tmp=`ls -l $rpath`
    [ -z "$tmp" ] && { echo "WARNING: $rpath: no such device"; continue; }

    # Make sure device is not reserved

    flag=0
    for res in $reserved
    do
        [ $dev = $res ] && { flag=1; break; }
    done
    [ "$flag" -eq 1 ] && { echo "WARNING: $dev is a reserved device"; continue; }

    # Make sure device is a disk

    i=`prtvtoc $rpath | grep '^[ ][ ]*2 ' |\
        sed -e 's;[ ][ ]*\/.*$;;'\
            -e 's;^.*[ ];;'`
    [ $i -eq 0 ] && { echo "WARNING: $dev is not a disk"; continue; }

    # Make sure each device has only one entry in $CF

    flag=0
    if [ -z "$devlist" ]
    then
         devlist=$dev
    else
        for device in $devlist
        do
            [ "$dev" = "$device" ] && { flag=1; break; }
        done
    fi
    if [ "$flag" -eq 1 ]
    then
        echo "$dev: duplicate entry: removing from $CF"
        continue
    else
        devlist="$devlist $dev"
    fi

    # prompt to get user's confirmation

    if [ $prompt -eq 0 ]
    then
        echo "Are you sure you want to test /dev/rdsk/$dev? All data will be lost! [yes/no]"
	read confirm
        while [ $confirm != "yes" ] && [ $confirm != "no" ]
        do
            echo "Please input yes or no:"
            read confirm
        done
        [ $confirm = "no" ] && { echo "WARNING: $dev is skipped."; continue; }
        [ $confirm = "yes" ] && { slice_flag=1; }
    else
	slice_flag=1
    fi

    # Set configuration defaults

    cnt=$MAXSLICE
    type="r"
    mount=""
    fs=0
    raw=1
    export=0

    # Read configuration options

    flag=0
    if [ "$dev" != "$line" ]
    then
        line=${line#* }
        for arg in $line
        do
            case "$arg"
            in
                [0-9]* )   cnt=$arg
                           [ $cnt -lt 1 ] && { flag=1; break; }
                           [ $cnt -gt $MAXSLICE ] && cnt=$MAXSLICE;;
                [e]      ) type="e";  fs=1; raw=0; export=1;;
                [f]      ) type="f";  fs=1; raw=0;;
                [r]      ) type="r";  fs=0; raw=1;;
                "er"|"re") type="er"; fs=1; raw=1;;
                "fr"|"rf") type="fr"; fs=1; raw=1;;
                 *       ) mount=$arg;;
            esac
        done
    fi
    [ "$flag" -eq 1 ] && { echo "$dev: 0 slices: removing from $CF"; continue; }
    [ "$fs" -eq 1 -a -z "$mount" ] && mount=$MOUNT
    echo "$dev $cnt $type $mount" >> tmp.$$

    # Slice the VTOC

    [ "$verbose" -eq 1 ] && echo "slicing $dev"

    (echo $dev; echo $cnt; prtvtoc $rpath) | awk '
        BEGIN { last_sector = 0
              }
         NR == 1  { dev = $1
                    next
                  }
         NR == 2  { cnt = $1
                    next
                  }
        $1 == "*" { print $0
                    if ($3 == "sectors/cylinder")
                        sec_per_cyl = $2
                    if ($3 == "accessible"){
                        acces_cyl = $2
			last_sector = (acces_cyl * sec_per_cyl) - 1
		    }
                  }
        END { maxi = cnt
              tag = 0
              flags = "00"
              slice_size = (last_sector - (3 * sec_per_cyl) + 1) / cnt
              cyl_per_slice = int (slice_size / sec_per_cyl)
              slice_size = cyl_per_slice * sec_per_cyl
              sec_count = slice_size
              print slice_size >> "size.slice"
              last_sec = (3 * sec_per_cyl) - 1
              for (i = 0; i <= maxi; ++i)
              {
                  first_sec = last_sec + 1
                  if (i == maxi - 1)
                      sec_count = last_sector - first_sec + 1
                  if (i == maxi)
                  {
                      first_sec = 0
                      sec_count = sec_per_cyl
                      tag = "1"
                      flag = "01"
                  }
                  last_sec = first_sec + sec_count - 1
                   
                  printf "%8s%7s%6s%11s%10s%10s\n",\
                      i, tag, flags, first_sec, sec_count, last_sec
              }
            }' > vtoc.$dev
done

[ "${slice_flag}" -eq 0 ] && { echo "No slices to make!" ; exit 0; }

mv tmp.$$ $CF  # $CF now has only complete and valid entries

# Build any missing device nodes

[ "$verbose" -eq 1 ] && echo "Building any missing device nodes"


disks

# Write the VTOCs

for file in `ls -C -1 vtoc.*`
do
    dev=${file##*.}
    [ "$verbose" -eq 1 ] && echo "Writing VTOC to $dev"
    fmthard -s $file /dev/rdsk/${dev}s2 >/dev/null
done

# Save original vfstab and dfstab

[ -f /etc/vfstab.orig ]     || cp /etc/vfstab /etc/vfstab.orig
[ -f /etc/dfs/dfstab.orig ] || cp /etc/dfs/dfstab /etc/dfs/dfstab.orig

# Make the filesystems, mountpoints, targets and cleanup files.
# Mount the filesystems and create vfstab entries for them.
# If exporting the filesystems, create dfs entries for them,
# start the NFS daemons and export the filesystems.

flag=0
daemons=0
IFS="
"

for line in `cat $CF`
do
    dev=${line%% *}
    rpath="/dev/rdsk/${dev}s2"
    line=${line#* }
    max_slice=${line%% *}
    line=${line#* }
    type=${line%% *}
    flag=0

    case "$type"
    in
        "fr" ) name=${line#* }
               fs=1
               raw=1
               export=0;;
        "er" ) name=${line#* }
               fs=1
               raw=1
               export=1;;
        [e]  ) name=${line#* }
               fs=1
               raw=0
               export=1;;
        [f]  ) name=${line#* }
               fs=1
               raw=0
               export=0;;
        [r]  ) fs=0
               raw=1
               export=0;;
    esac

    [ "$fs" -eq 1 ] && echo $name >> fs.$$

    bpath="/dev/dsk/$dev"
    mount="/${name}.$dev"
    if share | /usr/xpg4/bin/grep -q " ${mount}s"
    then
        slice=0
        while [ "$slice" -le "$MAXSLICE" ]
        do
            if share | /usr/xpg4/bin/grep -q " ${mount}s$slice "
            then
                [ "$verbose" -eq 1 ] && echo "${mount}s$slice is shared.  Unsharing it."
                unshare ${mount}s$slice
            fi
            slice=slice+1
        done
    fi

    if df -k | /usr/xpg4/bin/grep -q "^${bpath}s"
    then
        slice=0
        while [ "$slice" -le "$MAXSLICE" ]
        do
            if df -k | /usr/xpg4/bin/grep -q "^${bpath}s$slice"
            then
                [ "$verbose" -eq 1 ] && echo "${bpath}s$slice is mounted.  Unmounting it."
                umount ${bpath}s$slice
            fi
            slice=slice+1
        done
    fi

    if df -k | /usr/xpg4/bin/grep -q " ${mount}s"
    then
        slice=0
        while [ "$slice" -le "$MAXSLICE" ]
        do
            if df -k | /usr/xpg4/bin/grep -q " ${mount}s$slice"
            then
                [ "$verbose" -eq 1 ] && echo "${mount}s$slice is mounted.  Unmounting it."
                umount ${mount}s$slice
            fi
            slice=slice+1
        done
    fi

    if /usr/xpg4/bin/grep -q "^${bpath}s" /etc/vfstab
    then
        [ "$verbose" -eq 1 ] && echo "$bpath is in vfstab.  Removing entries"
        sed "/^\/dev\/dsk\/${dev}s/d" /etc/vfstab > tmp.$$
        mv tmp.$$ /etc/vfstab
    fi

    if /usr/xpg4/bin/grep -q "	${mount}s" /etc/vfstab
    then
        [ "$verbose" -eq 1 ] && echo "$mount is in vfstab.  Removing entries"
        sed "/	\\${mount}s/d" /etc/vfstab > tmp.$$
        mv tmp.$$ /etc/vfstab
    fi

    if [ "$export" -eq 1 ]
    then
        if /usr/xpg4/bin/grep -q " ${mount}s" /etc/dfs/dfstab
        then
            [ "$verbose" -eq 1 ] && echo "$mount is in dfstab.  Removing entries"
            sed "/ \\${mount}s/d" /etc/dfs/dfstab > tmp.$$
            mv tmp.$$ /etc/dfs/dfstab
        fi
        if [ "$daemons" -eq 0 ]
        then
            daemons=1
            release=`uname -r`
            if [ "$release" = "5.9" ]
            then
                /etc/init.d/nfs.server stop # in case it's already running
                /etc/init.d/nfs.server start
            elif [ "$release" = "5.10" ]
            then
		/usr/sbin/svcadm restart svc:/network/nfs/server:default
            fi
        fi
    fi

    slice=0

    while [ "$slice" -lt "$max_slice" ]
    do
        bpath="/dev/dsk/${dev}s$slice"
        rpath="/dev/rdsk/${dev}s$slice"

        if [ \( "$fs" -eq 1 -a "$raw" -eq 0 \) -o \( "$fs" -eq 1 -a "$flag" -eq 0 \) ]
        then
            flag=1
            mount="/${name}.${dev}s$slice"
            [ "$verbose" -eq 1 ] && echo "Making filesystem on $rpath"
            echo "y" | newfs $rpath > /dev/null 2>&1
            [ -d "$mount" ] || mkdir $mount
            [ "$verbose" -eq 1 ] && echo "mounting $bpath on $mount"
            mount $bpath $mount
            #echo "$bpath\t$rpath\t$mount\tufs\t2\tyes\t-" >> /etc/vfstab

            [ "$export" -eq 1 ] && share -F nfs -o rw,anon=0 $mount

            if [ "$export" -eq 1 -a "$raw" -eq 1 ]
            then
                echo "$mount" >> targets.client
            else
                echo "$mount" >> targets
            fi
            echo "rm ${mount}/0x*" >> cleanup
        else
            flag=0
            if [ "$export" -eq 1 -a "$raw" -eq 1 ]
            then
                echo "$rpath" >> targets.server
            else
                echo "$rpath" >> targets
            fi
        fi
        slice=slice+1
    done
done

[ -f cleanup ] && chmod u+x cleanup

# Determine the optimum value for MAX_BLOCKS
# and put this in env.s+h

[ "$verbose" -eq 1 ] && echo "Determining the optimum value for MAX_BLOCKS"

[ -f tmp.$$ ] && rm tmp.$$

if [ -f fs.$$ ] # Using filesystem(s) so use size from df command
then
    flag=0
    str=""
    sort fs.$$ | uniq > tmp.$$
    for dev in `cat tmp.$$`
    do
        [ "$flag" -eq 0 ] && { flag=1; str="$dev"; continue; }
        str="|$str"
    done

    df -k | egrep "$str" | awk ' 
        NR == 1 { min_avail = $4
                }
                { avail = $4
                  if (avail < min_avail)
                      min_avail = avail
                }
            END { printf "%s\n",  min_avail
                }' > tmp.$$
fi

min_avail=0
[ -f tmp.$$ ] && min_avail=`cat tmp.$$`
min_avail=min_avail*2  # adjust for df's output being in kilobytes

# Now check the VTOCs for anything smaller

if [ -f size.slice ]
then
    sort size.slice | uniq > tmp.$$
    for i in `cat tmp.$$`
    do
        [ "$min_avail" -eq 0 -o "$min_avail" -gt i ] && min_avail=i
    done
fi

echo "MAX_BLOCKS=$min_avail\nexport MAX_BLOCKS\n" > env.s+h

chmod u+x env.s+h
[ "$debug" -eq 0 ] && cleanup


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ end of config.s+h  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

