#!/bin/sh # # $Id: vxclone,v 1.2 2003-06-03 09:41:21-07 rpr Exp $ # # Script: vxclone.sh # # Purpose: This script creates a non-encapsulated boot drive from # an encapsulated veritas boot drive(s). This script is # specific to Sun Solaris 2.x at this time. # # Author: Rodney P. Rutherford # # Born on Date: August 09, 1999 # # Modification History: # # 1.0 - Tue Aug 17 18:47:23 PDT 1999 - Rodney Rutherford # - Original script created and tested # # 1.1 - Fri Aug 20 16:34:11 PDT 1999 - Rodney Rutherford # - Corrections made to vfstab creation # - Added check for syntax in config file # - Added check to make sure target filesystem was large enough # # 1.2 - Wed Aug 25 15:49:30 PDT 1999 - Rodney Rutherford # - corrected path to veritas device files # #------------------------------------------------------------------------------ VERSION='1.2 -- Wed Aug 25 15:49:30 PDT 1999' COPYRIGHT='Copyright (C) 1999' AUTHOR='Rodney P. Rutherford ' SCRIPT=`basename $0` #------------------------------------------------------------------------------ # # Admin environment variables. Update these per the local environment: # # MAILID - The email address(es) to mail errors to # PAGER - The email address(es) for a pager # PRINTER - The printer you wish to print to # PRINT - The print command to use. If you do not want any special formatting # or pre-processing of the log, just use 'cat'. # COPIES - The number of copies to print # LOGDIR - The directory path for the log files # LOG - The fullpath to the log file # # Examples: # # MAILID='root, user@domain.name' # PAGER=oncall # PRINTER=printer1 # PRINT='/usr/openwin/bin/mp -o' # COPIES=1 # LOGDIR=/var/log # LOG=${LOGDIR}/${SCRIPT}.log # # NOTE: # If you do not wish to have the script print, email the # admin(s), or page, then leave the variable set to null, # ie: PRINTER='' # # MAILID=root PAGER='' PRINTER='' PRINT='cat' COPIES=1 LOGDIR=/var/log LOG=$LOGDIR/${SCRIPT}.log # #------------------------------------------------------------------------------ # Application environment variables. Update these per the local environment: # # The following is the default location for the config file # It can be overridden on the command line via the -f option # CFGFILE=/etc/vxclone.conf # # #------------------------------------------------------------------------------ # Fixed environment variables. These normally should not need updating. # PATH=/usr/sbin:/usr/bin:/usr/ucb HOST=`hostname` DAY=`date +%d` DATE=`date +%y%m%d%H%M` HOUR=`date +%H` LOGDATE=`date +%Y%m%d` MTH=`date +%y%m-%B` CRONPID=`ps -e | grep cron | awk '{print $1}'` PID=$$ STATUS=0 TIME=`date +%H%M` TIMESTAMP=`/usr/bin/date` TTY=`tty` ID=`id` USER=`expr "${ID}" : 'uid=\([^(]*\).*'` LOCK=/var/spool/locks/${SCRIPT}.lock.${PID} # #========================= End of Configuration =============================== # #========================== Start of Functions ================================ # show_license () { echo "" echo " $SCRIPT version $VERSION" echo " $COPYRIGHT $AUTHOR" echo " This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. " } show_usage () { echo "" echo " $SCRIPT version $VERSION" echo " $COPYRIGHT $AUTHOR" echo "" echo " $SCRIPT will clone the system boot drives that are Veritas" echo " encapsulated to a non-encapsulated drive. This drive can then" echo " be used to boot the system for disaster recovery purposes." echo "" echo " Usage: $SCRIPT [-f config file] [-h] [-l]" echo "" echo "\t[-f config file] - The config file for the source/target drives" echo "\t\tThe default is ${CFGFILE}" echo "" echo "\t[-h] - Show this usage message." echo "" echo "\t[-l] - Show the license info." echo "" echo "" } echo_tty () { STRING=$1 echo "\n${STRING}" if [ "$TTY" != "not a tty" ] then echo "\n${STRING}" > $TTY fi } rotate_log () { test -f $LOG.2 && mv $LOG.2 $LOG.3 test -f $LOG.1 && mv $LOG.1 $LOG.2 test -f $LOG.0 && mv $LOG.0 $LOG.1 test -f $LOG && mv $LOG $LOG.0 } cleanup_exit () { # # Set message based on status and log # case $STATUS in 0) MESSAGE="Cloning has been completed.";; 11) MESSAGE="WARNING - another lockfile exists, skipping.";; 12) MESSAGE="WARNING - ${SCRIPT} is already running, skipping.";; 50) MESSAGE="ERROR - target filesystem is too small!";; 51) MESSAGE="ERROR - newfs command FAILED!";; 52) MESSAGE="ERROR - fsck FAILED!";; 53) MESSAGE="ERROR - mount FAILED!";; 54) MESSAGE="ERROR - ufsrestore FAILED!";; 55) MESSAGE="ERROR - installboot FAILED!";; 56) MESSAGE="ERROR - umount FAILED!";; 57) MESSAGE="ERROR - rmdir FAILED!";; 58) MESSAGE="ERROR - config file ${CFGFILE} does not exist!";; 59) MESSAGE="ERROR - config file ${CFGFILE} needs to be configured!";; 60) MESSAGE="ERROR - swap target in config file ${CFGFILE} is not valid!";; 61) MESSAGE="ERROR - filesystem $FS in config file ${CFGFILE} is not valid!";; 62) MESSAGE="ERROR - source $SOURCE in config file ${CFGFILE} is not valid!";; 63) MESSAGE="ERROR - target $TARGET in config file ${CFGFILE} is not valid!";; 99) MESSAGE="ERROR - script aborted or killed!";; 255) MESSAGE="ERROR - script FAILED with unknown error!";; esac echo_tty "\t$MESSAGE" # # Make sure all temporary mounts have been unmounted # MOUNTS=`mount | grep -c /tmp/vxclone` if [ $MOUNTS -gt 0 ] then for fs in `mount | grep /tmp/vxclone | awk '{print $1}'` do echo_tty "\tUnmounting the $fs mount point..." umount $fs rmdir $fs done fi # # Remove the temporary mount dir # test -d /tmp/vxclone && rmdir /tmp/vxclone # # Echo exit messages # test -n "$PRINTER" && echo_tty "\tPrinting logfile to printer ${PRINTER}..." echo_tty "\tRemoving lock file and exiting..." echo_tty "$HOST: The $SCRIPT script exited `date`" echo_tty "See $LOG for details." echo_tty "" # # Now record results in /var/adm/messages and then email/page/print # the log and status as necessary. # SUBJECT="$HOST: $SCRIPT $MESSAGE `date`" test -n "$MAILID" && mailx -s "$SUBJECT" $MAILID < $LOG test "$STATUS" -gt "49" && test -n "$PAGER" && echo "$SUBJECT" | mailx $PAGER test -n "$PRINTER" && $PRINT $LOG | lp -d $PRINTER -n $COPIES > /dev/null logger -p user.err "$SCRIPT - $MESSAGE" logger -p user.err "$SCRIPT - See $LOG for details." # # Remove lock file and exit with correct status # test -f $LOCK && rm $LOCK sleep 3 exit $STATUS } error_check () { if [ $? -ne 0 ] then if [ $# -eq 1 ] then STATUS=$1 else STATUS=255 fi export STATUS fi if [ $STATUS -gt 0 ] then cleanup_exit fi } # #========================= End of Functions =============================== # #====================== Start of Main Program ============================= # # # Set default file permissions # umask 022 # # Check to make sure the script is being run as root # if [ "$USER" != "0" ] then echo "" echo "\tERROR: You must be root to run $0" echo "" echo "\tExiting..." echo "" exit 1 fi # # Get any command line arguments specified # if [ $# -gt 0 ] then while getopts f:hl option do case $option in f) CFGFILE=$OPTARG if [ ! -r "$CFGFILE" ] then echo "" echo "\tERROR: The config file ${CFGFILE} was not found!" echo "" echo "\tExiting..." echo "" exit 1 fi ;; h) show_usage; exit 1;; l) show_license; exit 1;; \?) show_usage; exit 1;; esac done fi # # Trap for HUP INT QUIT ABRT TERM # Execute the cleanup_exit function when trapped. # trap 'STATUS=99; cleanup_exit' 1 2 3 6 15 # # Clear the screen to easily see any install messages # clear # # Redirect all output to a log file. This will overwrite any existing log # file of the same name. First we have to make sure the log directory exists # and rotate any existing log files. # if [ ! -d $LOGDIR ] then mkdir -m 755 -p $LOGDIR error_check fi rotate_log exec > $LOG 2>&1 # # Echo starting messages # logger -p user.err "$SCRIPT - Started" echo_tty "$HOST: The $SCRIPT script started `date`" # # Test to make sure there is not a lockfile # and that script is not already running. # echo_tty "\tChecking/creating lock file..." ED='vi|view|more|less|head|tail' COUNT=`ps -ef | grep $SCRIPT | egrep -v "grep|$ED|$CRONPID|$PID" | wc -l` if [ -f /var/spool/locks/${SCRIPT}.lock.* ] then STATUS=11 echo "\nA lockfile exists already. It appears that another $SCRIPT" echo "script may already be running. If not, remove the lockfile(s) and" echo "run $SCRIPT again. Be sure to cleanup any old tmp file(s)" echo "(/tmp/${SCRIPT}.*) that may have been left as well." echo "The current lockfiles are:\n" ls /var/spool/locks/${SCRIPT}.lock.* echo "" for id in `ls /var/spool/locks/${SCRIPT}.lock.*` do if [ $id != $LOCK ] then ID=`cat $id` echo "\nThe process ID referenced by the lockfile(s) is: ${ID}" echo "\nProcess ID info for process $ID is:\n" ps -ef | grep $ID | grep -v grep echo "" fi done cleanup_exit elif [ "$COUNT" -gt "0" ] then STATUS=12 echo "\nA ${SCRIPT} process is already running." echo "\nProcess ID info is:\n" ps -ef | grep ${SCRIPT} | grep -v grep echo "" cleanup_exit else echo $$ > $LOCK fi # # Verify the config file exists and check for proper syntax # echo_tty "\tChecking status of config file..." if [ ! -r $CFGFILE ] then STATUS=58 cleanup_exit else LINES=`cat $CFGFILE | grep -v '^#' | sed '/^$/d' | wc -l` if [ $LINES -eq 0 ] then STATUS=59 cleanup_exit else echo_tty "\tChecking syntax of config file..." for fs in `cat $CFGFILE | grep -v '^#' | cut -d# -f1 | sed '/^$/d'` do FS=`echo $fs | awk -F: '{print $1}'` SOURCE=`echo $fs | awk -F: '{print $2}'` TARGET=`echo $fs | awk -F: '{print $3}'` if [ "$FS" = "swap" ] then if [ ! -b "/dev/dsk/$TARGET" ] then STATUS=60 cleanup_exit fi else if [ "$FS" != "`/bin/df $FS | awk '{print $1}'`" ] then STATUS=61 cleanup_exit else if [ ! -b "/dev/vx/dsk/$SOURCE" ] then STATUS=62 cleanup_exit elif [ ! -b "/dev/dsk/$TARGET" ] then STATUS=63 cleanup_exit fi fi fi done fi fi # # Get the list of filesystems to copy # for fs in `cat $CFGFILE | grep -v '^#' | cut -d# -f1 | sed '/^$/d'` do FS=`echo $fs | awk -F: '{print $1}'` SOURCE=`echo $fs | awk -F: '{print $2}'` TARGET=`echo $fs | awk -F: '{print $3}'` if [ "$FS" = "swap" ] then echo_tty "\tThe swap filesystem will be on partition ${TARGET}..." else MNT=/tmp/vxclone/${TARGET} SOURCESIZE=`devinfo -p /dev/vx/rdsk/$SOURCE | awk '{print $5}'` TARGETSIZE=`devinfo -p /dev/rdsk/$TARGET | awk '{print $5}'` echo_tty "\tVerifying filesystem capacity on target slice ${TARGET}..." if [ "$TARGETSIZE" -lt "$SOURCESIZE" ] then STATUS=50 cleanup_exit fi echo_tty "\tCreating new filesystem on target slice ${TARGET}..." echo "" newfs /dev/rdsk/$TARGET < /dev/null error_check 51 echo_tty "\tChecking new filesystem on target slice ${TARGET}..." echo "" fsck -o p /dev/rdsk/$TARGET < /dev/null error_check 52 echo_tty "\tMounting new filesystem on temporary location ${MNT}..." test ! -d $MNT && mkdir -p $MNT mount /dev/dsk/${TARGET} $MNT error_check 53 echo_tty "\tRestoring the new filesystem from ${SOURCE}..." echo "" ufsdump 0f - $FS | (cd $MNT; ufsrestore -rf -) error_check 54 echo_tty "\tRemoving the restoresymtable from ${TARGET}..." test -f $MNT/restoresymtable && rm $MNT/restoresymtable if [ "$FS" = "/" ] then echo_tty "\tCreating the new bootblock on ${TARGET}..." installboot /usr/platform/`uname -i`/lib/fs/ufs/bootblk /dev/rdsk/$TARGET error_check 55 echo_tty "\tCreating the new vfstab file on ${TARGET}..." for fs in `cat $CFGFILE | grep -v '^#' | cut -d# -f1 | sed '/^$/d'` do VFS=`echo $fs | awk -F: '{print $1}'` VSOURCE=`echo $fs | awk -F: '{print $2}'` VTARGET=`echo $fs | awk -F: '{print $3}'` if [ "$VFS" = "swap" ] then echo "/dev/dsk/${VTARGET}\t-\t-\tswap\t-\tno\t-" >> /tmp/${SCRIPT}.swapfs else if [ "$VFS" = "/" ] || [ "$VFS" = "/usr" ] || [ "$VFS" = "/var" ] then echo "/dev/dsk/${VTARGET}\t/dev/rdsk/${VTARGET}\t$VFS\tufs\t1\tno\t-" >> /tmp/${SCRIPT}.rootfs else echo "/dev/dsk/${VTARGET}\t/dev/rdsk/${VTARGET}\t$VFS\tufs\t2\tyes\t-" >> /tmp/${SCRIPT}.rootfs fi fi done mv $MNT/etc/vfstab $MNT/etc/vfstab.veritas echo "# #device device mount FS fsck mount mount #to mount to fsck point type pass at boot options # #/dev/dsk/c1d0s2 /dev/rdsk/c1d0s2 /usr ufs 1 yes - # fd - /dev/fd fd - no - /proc - /proc proc - no -" > $MNT/etc/vfstab cat /tmp/${SCRIPT}.swapfs >> $MNT/etc/vfstab sort +3 /tmp/${SCRIPT}.rootfs >> $MNT/etc/vfstab /bin/rm /tmp/${SCRIPT}.* TMP=`grep -c /tmp $MNT/etc/vfstab` if [ "$TMP" -eq "0" ] then echo "swap\t-\t/tmp\ttmpfs\t-\tyes\t-" >> $MNT/etc/vfstab fi echo_tty "\tUpdating the /etc/system file on ${TARGET}..." mv $MNT/etc/system $MNT/etc/system.veritas sed -e '/^rootdev/d' -e '/^set vxio:/d' /etc/system > $MNT/etc/system fi echo_tty "\tUnmounting the new filesystem from ${MNT}..." umount $MNT error_check 56 echo_tty "\tRemoving temporary mount point ${MNT}..." rmdir $MNT error_check 57 echo_tty "\tRunning final fsck on target slice ${TARGET}..." echo "" fsck -o p /dev/rdsk/$TARGET < /dev/null error_check 52 fi done # # Check for successful completion and log, cleanup tmp files, and exit # cleanup_exit