Listing 1: The mfsck script

#!/bin/sh
#
# File:		mfsck.sh (Check File Systems on multiple disks)
# Version:	@(#)mfsck	1.10 25 May 1994 1
#
# Copyright 1994 Sun Microsystems, Inc.  Permission to copy, use, distribute,
# modify and incorporate this work in Sys Admin
# in any form is hereby granted.  Readers of Sys Admin
# may copy, use, and distribute this work
# subject to the following conditions:
# 
#   1) The document may be used for non-commercial purposes only; and
#   2) Any copy of this work or portion thereof must include a copy of this
#      copyright notice.
# 
# This work is provided as is without warranty of any kind, either express or
# implied, including, but not limited to, the implied warranties of
# merchantability, fitness for a particular purpose, or non-infringement.
# 
# This work could include technical inaccuracies or typographical errors.
# Changes are periodically added to the information herein; these changes
# will be incorporated in new additions of the publication.
#
# Sun Microsystems, Inc. may make improvements and/or changes in its products
# and/ or its programs including the program routines described in this work
# at any time.
#
# Synopsis:
# ---------
# mfsck <task file>
#
# where,
#   <task file>  is the name of a file containing the names of the disks and
#		their partitions/file systems to check.
#
# Example:
# --------
#   mfsck task_file
#
# Exits:
# ------
#   = 0,	success
#   <>0,        failure
#
# Notes:
# ------
# -<task file> Format:
#    <logical device> <partition> ... <partition>
#
#  e.g.,
#    c0t5d0	0 1 6
#
# -'CHKFile' used to verify that a process is still active.
# -'WIPFile' is a "work-in-progress" file.
#
# *****************
# *** Functions ***
# *****************
#
# ************************
# *** Function: CkExit ***
# ************************
#
# 'fsck' EXIT CODE
# ----------------
#    0         file system is okay and does not need checking
#    1         erroneous parameters are specified
#    32        file system is unmounted and needs checking  (fsck -m only)
#    33        file system is already mounted
#    34        cannot stat device
#    36        uncorrectable errors detected - terminate normally
#    37        a signal was caught during processing
#    39        uncorrectable errors detected - terminate  immediately
#    40        for root, same as 0.
#    50        removing log file
#
# Arguments:
# ----------
# 0  Exit Code
#
CkExit () {
   #
   case "$1" in
      0) ;;
      1) EMSG="Erroneous parameters are specified"
         echo "$EPROMPT$EMSG"
         ;;
      32) EMSG="File system is unmounted and needs checking  (fsck -m only)"
         echo "$EPROMPT$EMSG"
         ;;
      33) EMSG="File system is already mounted"
         echo "$EPROMPT$EMSG"
         ;;
      34) EMSG="Cannot stat device"
         echo "$EPROMPT$EMSG"
         ;;
      36) EMSG="Uncorrectable errors detected - terminate normally"
         echo "$EPROMPT$EMSG"
         ;;
      37) EMSG="A signal was caught during processing"
         echo "$EPROMPT$EMSG"
         ;;
      39) EMSG="Uncorrectable errors detected - terminate immediately"
         echo "$EPROMPT$EMSG"
         ;;
      40) EMSG="For root, same as 0"
         echo "$EPROMPT$EMSG"
         ;;
      *) EMSG="Unknown response from 'fsck'; Received: $1"
         echo "$EPROMPT$EMSG"
         echo "  Please Check"
         ;;
   esac
   #
}	# *** END: CkExit ***
#
# *****************
# *** Main Code ***
# *****************
#
# **********************
# *** Initialization ***
# **********************
PID=$$
#
EPROMPT="ERROR>"
PROMPT=">"
#
NARGS=`expr 1`
#
ERR_SYNTAX=`expr 50`
ERR_TFN_OF=`expr 52`	# The task file specified is NOT an ordinary file
ERR_TFN_NRD=`expr 54`	# The task file specified is NOT readable
ERR_TFN_EPTY=`expr 56`	# The task file specified is empty
ERR_CF_NZRO=`expr 60`	# The control file exists AND is a non-ZERO file
ERR_WIP_EXISTS=`expr 62` # WIP file exists as an ordinary file or a directory
ERR_WIP_CREATE=`expr 64` # creating the WIP file
ERR_CHK_EXISTS=`expr 66` # CHK file exists as an ordinary file or a directory
ERR_CHK_CREATE=`expr 68` # creating the CHK file
ERR_LOGF_EXISTS=`expr 70` # log file for partition exists
ERR_LOGF_CREATE=`expr 72` # creating log file for partition
ERR_RM_LOGFS=`expr 76`	# removing log files

#
#
# ***********************
# *** Input Arguments ***
# ***********************

if [ "$#" -ne "$NARGS" ] ; then
   echo "$EPROMPT Incorrect Number of Input Parameters; Exp: $NARGS, Obs: $#"
   echo "synopsis:"
   echo "  mfsck <task file>"
   echo "where,"
   echo "  <task file>  is the name of a file containing the disks and the"
   echo "               file systems to check"
   echo
   echo "Notes:"
   echo "------"
   echo "  -Syntax of the file <task file> :"
   echo "     #         comment line"
   echo "     <logical device> <partition> ... <partition>"
   echo
   echo "   Example:"
   echo "   --------"
   echo "     c0t5d0 0 1 6"
   echo
   exit $ERR_SYNTAX
fi
#
CWD=`pwd`	# *** current working directory ***
#
RES=`expr 0`
RES1=`expr 0`
RES2=`expr 0`
EXE="999"
#
SleepSec=`expr 60`
#
SP=" "
PER="."
#
ASTER="*"
SLASH="/"
PIPE="|"
BKGND="&"
SQUOTE="'"
DQUOTE='"'
CQUOTE=$SQUOTE$DQUOTE$SQUOTE
#
EQUALS="="
PERIOD="."
#
NullList=""
#
ERRLIS=""	# error list
FERRLIS=""	# 'fsck' error list
#
FSCKCMD="fsck"
#
NDSK=`expr 0`	# number of disks
CDSK=`expr 0`	# scanning number of disks
#
CNTL="cntl"
CTRLF="$FSCKCMD$PERIOD$CNTL$PERIOD$PID"
#
F_PID=`expr 0`
#
WIP="wip"
WIPFile="$CWD$SLASH$FSCKCMD$PERIOD$WIP$PERIOD$PID"
#
CHK="chk"
CHKFile="$CWD$SLASH$FSCKCMD$PERIOD$CHK$PERIOD$PID"
#
LOG="log"
LOGFile="$CWD$SLASH$FSCKCMD$PERIOD$LOG$PERIOD$PID"
#
TFN=$1		# get the task file name
#
if [ ! -f "$TFN" ] ; then
   echo "$EPROMPT The task file specified ( $TFN ) is NOT an ordinary file"
   echo "  Please re-check and re-start"
   exit $ERR_TFN_OF
fi
#
if [ ! -r "$TFN" ] ; then
   echo "$EPROMPT The task file specified ( $TFN ) is NOT readable"
   echo "  Please re-check and re-start"
   exit $ERR_TFN_NRD
fi
#
if [ ! -s "$TFN" ] ; then
   echo "$EPROMPT The task file specified ( $TFN ) is an empty file"
   echo "  Please re-check and re-start"
   exit $ERR_TFN_EPTY
fi
#
# ***************************
# *** Check: Control File ***
# ***************************
# The control file should not exist.  If it does and is non-ZERO, this is an
# error.
#
if [ -s "$CTRLF" ] ; then
   echo "$EPROMPT The control file $CTRLF exists AND is a non-ZERO file"
   echo "  Please re-check and re-start"
   exit $ERR_CF_NZRO
fi
#
rm "$CTRLF" 1>/dev/null 2>&1
touch "$CTRLF"
#
# *** Identify the Current Process ID ***
echo "The Current Process ID = $PID"
echo "File name extensions will contain this as identification"
#
# ******************************************
# *** Create the "work-in-progress" file ***
# ******************************************
if [ -f "$WIPFile" -o -d "$WIPFile" ] ; then
   # WIP file already exists as an ordinary file or a directory.
   # This is an error condition!!!
   echo "$EPROMPT WIP file already exists as an ordinary file or a directory"
   echo "  Aborting....."
   exit $ERR_WIP_EXISTS
else
   # the following code makes sure we are starting out with a ZERO file
   rm "$WIPFile" 1> /dev/null 2>&1	# delete it
   touch "$WIPFile"			# create it
   RES=$?
   if [ "$RES" -ne 0 ] ; then
      echo "$EPROMPT($RES) Creating the file: $WIPFile"
      exit $ERR_WIP_CREATE
   fi
fi
#
# ************************************
# *** Create the "Check PID File " ***
# ************************************
if [ -f "$CHKFile" -o -d "$CHKFile" ] ; then
   # CHK file already exists as an ordinary file or a directory.
   # This is an error condition!!!
   echo "$EPROMPT CHK file already exists as an ordinary file or a directory"
   echo "  Aborting....."
   exit $ERR_CHK_EXISTS
else
   # the following code makes sure we are starting out with a ZERO file
   rm "$CHKFile" 1> /dev/null 2>&1      # delete it
   touch "$CHKFile"                     # create it
   RES=$?
   if [ "$RES" -ne 0 ] ; then 
      echo "$EPROMPT($RES) Creating the file: $CHKFile"
      exit $ERR_CHK_CREATE
   fi
fi
#
# ********************
# *** Control Loop ***
# ********************
#
exec < $TFN	# redirect standard input to the task file name
#
while true
do
   read p0 p1 p2 p3 p4 p5 p6 p7 p8 REM
   RES=$?
   #
   # *****************************
   # *** Check: Any More Lines ***
   # *****************************
   # 'read' always returns an exit status of '0' unless an END-OF-FILE is
   # encountered.
   #
   if [ "$RES" -ne 0 ] ; then
      echo "$PROMPT Done reading from Task File: $TFN"
      break
   fi
   #
   # *** Check: Comment Line ***
   if [ "$p0" = "#"  -o "$p0" = "" ] ; then
      # ********************
      # *** Comment Line ***
      # ********************
      #
      # echo "$PROMPT Comment Line:"
      # echo "  $p0 $p1 $REM"
      #
      :	# NULL command
   else
      # *****************
      # *** Data Line ***
      # *****************
      # -<task file> Format:
      #    <logical device> [0] [1] [2] [3] [4] [5] [6] [7]
      #
      # echo "Device: $p0; Disk Type: $p1; Remainder: $REM"
      #
      DEV="/dev/rdsk/$p0"
      #
      # ******************************
      # *** Check the File Systems ***
      # ******************************
      NDSK=`expr $NDSK + 1`
      EXT="$NDSK$PERIOD$PID"
      #
      # **************************************
      # *** Merge the Specified Partitions ***
      # **************************************
      #
      case "$p1" in
         [0-7]) PART="s$p1"
            SUM="$DEV$PART "
            case "$p2" in
               [0-7]) PART="s$p2"
                  SUM="$SUM $DEV$PART "
                  #
                  case "$p3" in
                     [0-7]) PART="s$p3"
                        SUM="$SUM $DEV$PART "
                        #             
                        case "$p4" in
                           [0-7]) PART="s$p4"
                              SUM="$SUM $DEV$PART "
                              #
                              case "$p5" in
                                 [0-7]) PART="s$p5"
                                    SUM="$SUM $DEV$PART "
                                    #
                                    case "$p6" in
                                       [0-7]) PART="s$p6"
                                          SUM="$SUM $DEV$PART "
                                          #
                                          case "$p7" in
                                             [0-7]) PART="s$p7"
                                                SUM="$SUM $DEV$PART "
                                                #
                                                case "$p8" in
                                                   [0-7]) PART="s$p8"
                                                      SUM="$SUM $DEV$PART "
                                                      #
                                                      ;;
                                                   *) ;;
                                                   #
                                                esac
                                                #
                                                ;;
                                             *) ;;
                                             #
                                          esac
                                          #
                                          ;;
                                       *) ;;
                                       #
                                    esac
                                    #
                                    ;;
                                 *) ;;
                                 #
                              esac
                              #
                              ;;
                           *) ;;
                           #
                        esac
                        #
                        ;;
                        #
                     *) ;;
                     #
                  esac
                  #
                  ;;
                  #
               *) ;;
            esac
            #
            # *********************************************
            # *** Create the "log" file for this device ***
            # *********************************************
            LF="$LOGFile$PERIOD$NDSK"
            #
            if [ -f "$LF" -o -d "$LF" ] ; then
               # Log file already exists as an ordinary file or a directory.
               # This is an error condition!!!
               echo "$EPROMPT Log file already exists as an ordinary file or a directory"
               echo "  File:  $LF"
               echo "  Aborting....."
               exit $ERR_LOGF_EXISTS
            else
               # the following code makes sure we are starting out with a ZERO file
               rm "$LF" 1> /dev/null 2>&1      # delete it
               #
               MSG="Creating Log File: $LF"
               echo "$PROMPT$MSG"
               #
               touch "$LF"                     # create it
               RES=$?
               if [ "$RES" -ne 0 ] ; then
                  echo "$EPROMPT($RES) Creating the file: $LF"
                  exit $ERR_LOGF_CREATE
               fi
            fi
            #
            echo "Device:  $DEV" > $LF
            date >> $LF
            #
            # *******************************
            # *** Check: Partition Exists ***
            # *******************************
            CMD="prtvtoc $SUM"
            echo "Checking:  $SUM exists"
            eval "$CMD" > /dev/null      # execute the command
            RES2=$?
            if [ "$RES2" -ne 0 ] ; then
               # ****************************
               # *** Partition is Missing ***
               # ****************************
               EMSG="$SUM is missing"
               ERRLIST="$ERRLIST $EMSG ;"
               echo "$EPROMPT$EMSG"
               echo
               echo "$EPROMPT$EMSG" >> $LF
               echo >> $LF
               continue
            fi
            #  
            # *********************************
            # *** Check: Mounted Partitions ***
            # *********************************
            CMD="mount $PIPE grep $SUM"
            echo "Checking:  $SUM has mounted partitions"
            eval "$CMD" > /dev/null      # execute the command
            RES2=$?
            #
            if [ "$RES2" -eq 0 ] ; then
               # ***********************************
               # *** Disk has mounted partitions ***
               # ***********************************
               EMSG="$SUM has mounted partitions"
               ERRLIST="$ERRLIST $EMSG ;"
               echo "$EPROMPT$EMSG"
               echo
               echo "$EPROMPT$EMSG" >> $LF
               echo >> $LF
               continue
            fi
            #
            # ************************
            # *** Partition Exists ***
            # ************************
            #
            CMD="$FSCKCMD $SUM >> $LF &"
            echo "Executing Command:"
            echo "  $CMD"
            echo "Executing Command:" >> $LF
            echo "  $CMD" >> $LF
            eval "$CMD"	# execute the command in the background
            RES1=$?
            F_PID=$!       # get the background process ID
            #
            echo "$F_PID $p0" >> "$WIPFile"  # update the WIP file
            #                      
            if [ "$RES1" -ne 0 ] ; then
               EMSG="Checking File Systems Using the Command:"
               echo "$EPROMPT($RES1) $EMSG"
               echo "  $CMD"
               CkExit $RES1
               echo "  Ignoring this disk ($p0)............."
               #
               # *** Update the 'fsck' error list ***
               FERRLIST="$FERRLIST $DEV"
            else
               echo "$PROMPT Background Process ID $F_PID Started"
               # *** Update the Control File ***
               echo "$p0 $p1 $F_PID" >> $CTRLF
               #                      
            fi
            #
            ;;
            #
         *) echo "Argument 1 not valid"
            ;;
      esac
      #
      MSG="Device: $DEV has started"
      echo "  $MSG"
      #
      # **************************************
      # *** END: Processing Current Recond ***
      # **************************************
      #
   fi
   #
done	# END: WHILE
#
exec < /dev/tty		# redirect standard input
#
# *************
# *** Notes ***
# *************
# -Done reading the file with the devices.  Now monitor the processes doing the
#  checking.
#
# ********************
# *** Control Loop ***
# ********************
#
echo "> > > Waiting For ALL Processes to Complete < < <"
DoneF=`expr 0`	# Done Flag (=0, NO; <> 0, YES)
#
while true
do
   #
   # ***********************
   # *** Check: WIP File ***
   # ***********************
   exec < $WIPFile	# redirect standard input
   #
   Tmp1=`expr 0`	# temp flag (=0, no process active; <> 0, YES)
   #
   while true
   do
      # *** Get A Line ***
      # Format:
      #   <PID>   <raw device>
      #
      read p0 p1 REM
      RES=$?
      # *****************************
      # *** Check: Any More Lines ***
      # *****************************
      # 'read' always returns an exit status of '0' unless an END-OF-FILE is
      # encountered.
      if [ "$RES" -ne 0 ] ; then
         # **********************************
         # *** At the end of the WIP file ***
         # **********************************
         break
      fi
      #
      # ***********************************
      # *** Check: Process Still Active ***
      # ***********************************
      eval ps -ae $PIPE grep $p0 > $CHKFile
      CkRes=$?
      if [ -s "$CHKFile" ] ; then
         Tmp1=`expr 1`	# indicate some process active
         echo "$p0 still active on $p1"		# notify user
      fi
      #
   done
   #
   exec < /dev/tty         # redirect standard input
   #
   # *********************************
   # *** Check: Any Process Active ***
   # *********************************
   if [ "$Tmp1" -eq 0 ] ; then
      echo "All recorded background processes have completed"
      break
   fi
   #
   # **********************************
   # *** Wait Before Checking Again ***
   # **********************************
   sleep $SleepSec
   #
   echo "> > > Checking for Active Processes < < <"
   #
done
#
# *********************************************
# *** Scan the Log Files for Error Messages ***
# *********************************************
# -This code will scan all file with "$LOGFile$PERIOD" in the file name.
# -If a partition can't be found, a "Can't Stat" message will be placed
#  in a log file.
echo "Scanning for partitions that were not found; unable to check"
# echo "-log files will contain message:  Can\'t stat <raw partition>"
CMD="grep \"Can\'t stat\" $LOGFile$ASTER "
echo "Partitions NOT found if entries follow:"
eval "$CMD"      # execute the command
#
# Notes:
# ------
# -No checking is performed on this command.  Deemed non-critical. 
#
# ***************************
# *** Display Error Lists ***
# ***************************
if [ ! "$ERRLIST" = "$NullList" ] ; then
   echo
   echo "Found Startup Problems With:"
   echo "  $ERRLIST"
fi
if [ ! "$FERRLIST" = "$NullList" ] ; then
   echo
   echo "Found 'fsck' Problems With:"
   echo "  $FERRLIST"
fi
#
# ******************************
# *** Remove Temporary Files ***
# ******************************
rm $CHKFile 1> /dev/null 2>&1
rm $WIPFile 1> /dev/null 2>&1
#
echo
MSG="Do You Want to Remove all Log Files"
echo "$PROMPT$MSG"
MSG="This will delete all files with 'log' in the file name"
echo "$PROMPT$MSG"
MSG="Proceed (Y/N) ?"
echo "$PROMPT$MSG"
#
read ans rem
if [ "$ans" = "y" -o "$ans" = "Y" -o "$ans" = "yes" -o "$ans" = "Yes" -o "$ans" = "YES" ] ; then
   # ****************************
   # *** Remove All Log Files ***
   # ****************************
   #
   MSG="Removing Log Files"
   echo "$PROMPT$MSG"
   CMD="rm *\.log\.* 1> /dev/null 2>&1"
   eval "$CMD"
   RES=$?
   if [ "$RES" -ne 0 ] ; then
      EMSG="Removing Log Files"
      echo "$EPROMPT$EMSG"
      echo "  Response: $RES"
      exit $ERR_RM_LOGFS
   fi
   #
fi
#
# *****************************************
# *** Remove all Work-In-Progress Files ***
# *****************************************
#
MSG="Removing all Work-In-Progress Files"
echo "$PROMPT$MSG"
#
CMD="rm *\.wip\.* 1> /dev/null 2>&1"
eval "$CMD"
#
# ******************************
# *** Remove all Check Files ***
# ******************************
#
MSG="Removing all Check Files"
echo "$PROMPT$MSG" 
# 
CMD="rm *\.chk\.* 1> /dev/null 2>&1" 
eval "$CMD" 
#
# **************************************************
# *** Remove the Control File ONLY if successful ***
# **************************************************
#
MSG="Removing the Control File: $CTRLF"
echo "$PROMPT$MSG"
#
CMD="rm $CTRLF 1> /dev/null 2>&1"
eval "$CMD"
RES=$?
if [ "$RES" -ne 0 ] ; then
   EMSG="Removing Control File"
   echo "$EPROMPT$EMSG"
   echo "  Response: $RES"
fi
#
exit 0
#
#
# ***********
# *** END ***
# ***********

