#!/bin/csh -f # @(#) $Id: dodump,v 7.4 2000/06/24 20:06:49 myers Exp myers $ ####################################################################### # dodump - shell script to perform filesystem dumps -- MULTI-FILE VERSION # # This shell script automates the process of making backups of disk # filesystems. You simply need to answer a few questions about the # level of the dump and the name of the tape you are going to use, and # this script does the rest. It writes the dump tape, then rewinds # and reads from the tape a list of the files that were dumped. This # is both for verification and so that you can have an on-line table # of contents for the dump, should you ever need to restore a file. # # Instead of an interactive dialogue, you can also use command line # arguments to specify the level of the dump, the position on the # tape, and so forth. The command line format is: # # dodump [-L level] [-N tape-name] [-s files-to-skip] [-q] [-F] [-j] # [-f device] or [-f host:device] [-t] # # where # # -L level specifies the dump level (0 to 9) # -N tape-name specifies the name of the tape (for the DUMPDATES log) # -S files-to-skip indicates the position on the tape, # in terms of files to skip (so omitting this is the same # as saying "-s 0". # -f device specifies the tape device to dump to. This can also # be in the form host:device to dump to a remote host. # -q "quiet": do not log the dump in DUMPDATES # -t Use -t not -f for target of mt command # -d Dump device is a directory # -F "force": force a new dump, even if a previous dump has # not finished. # -j "eject": eject the tape when the dump is done. # -V version # # The behaviour of the script can also be modified by putting variable # settings in the file dodump.options, though the command line options # take precedence. Read the comments in the script for further details. # # If you specify a directory instead of a tape device then each # filesystem will be dumped to a separate file in that directory # # The original version of this script was a modification of the 'dumdum' # script in the book "Unix System Administration Handbook" by Nemeth, # Snyder and Seebass, but it has evolved greatly over the years. # Still, I am indebted to them for the initial framework. # # This particular version is designed for dumping several filesystems # onto one Exabyte or DDS tape. Previous versions were for dumping # a single filesystem onto several QUIC tapes. Times change. # # Eric Myers # Department of Physics, University of Texas at Austin 92/06/08 # Department of Physics and Astronomy, Vassar College 93/10/12 # Department of Physics, University of Michigan, Ann Arbor 96/08/19 ####################################################################### set PROG=`basename $0` ## Custom Configuration: # # DUMPDIR: directory where all dump information is stored. # Set this to your local dump directory. It can be modified # by the dodump.options file (but then DUMPDIR should point to where # that file is located). set DUMPDIR = "/usr/local/adm/dumpdir" # default if ( -d /private/adm/dumpdir ) set DUMPDIR = "/private/adm/dumpdir" # NeXT if ( -d /var/adm/dumpdir ) set DUMPDIR = "/var/adm/dumpdir" # POSIX if ( ! -d ${DUMPDIR} ) then echo "${PROG}: no such directory: ${DUMPDIR}" exit 6 endif ### # PATH: just what's needed, nothing else (for better security) # (do this after setting DUMPDIR) set path=( /usr/local/sbin /usr/local/bin /sbin /etc /usr/etc /bin /usr/bin /usr/ucb /usr/sbin $DUMPDIR ) umask 002 # Check for write access to the DUMPDIR as validation of user if ( ! ${?USER} ) set USER=`whoami` rm -f $DUMPDIR/.$PROG.lock ( echo "$$ ${USER}: dodump $*" > $DUMPDIR/.$PROG.lock ) >& /dev/null if ( $status ) then echo "You must be root or an operator to run $PROG." exit 2 endif ################################################## # PARAMETERS: set defaults here, and/or edit the file dodump.options. # The settings here are the defaults, but those in the file # dodump.options (in the directory set by DUMPDIR) will override. ## Filesystems to be dumped on level 0 and 1: set LOLEV = "root home usr usr/local " # These just the basics ## Filesystems to be dumped on higher levels: set ALWAYS = "home usr usr/local" ## Position on the dump tape to begin at (number of files to skip) set Nfsf = 0 ## Level of dump to do. If this is outside the allowed range (0-9) # then user is asked for a level from the dump schedule in WHICHDUMP set level = -1 ## dump program arguments set NOTIFY = "" # set to "n" to notify operators of a problem set LOGDUMP = "u" # set to u to log in /etc/dumpdates ## device to dump to: ( use /dev/nrst# with NO REWIND) set DUMPDEV = "/dev/rmt/0mnb" # local tape device - HP-UX set DUMPDEV = "/dev/ht0" # local tape device - on Linux set DUMPDEV = "/dev/nrst0" # local tape device - Sun/NeXT ## mt command for mag tape control (use 'rsh host mt' for remote machines) set MTCMD = "mt -f $DUMPDEV " # local mag tape commands ## dump program to use: set DUMP = "dump" ## restore program to use: (used for creating dump table of contents) set RESTORE = "restore" # local restore ## effective size of the tape (in feet): #set SIZE = "600" # DC600A tapes are 600ft #set SIZE = "6000" # 2.3-Gbyte 8mm Exabyte #set SIZE = "4150" # 2.0-Gbyte 4mm DDS #set SIZE = "7700" # 4.0-Gbyte 4mm DDS set SIZE = "14300" # Linux: 4.0-Gbyte 4mm DDS ## effective density of tape (bpi): #set DEN = "1250" # QIC-150 tapes are 1250bpi #set DEN = "54000" # 2.3-Gbyte 8mm Exabyte #set DEN = "550000" # 2.0-Gbyte 4mm DDS #set DEN = "815000" # 4.0-Gbyte 4mm DDS set DEN = "54000" # Linux: 4.0-Gbyte 4mm DDS ## name of tape (begin with unknown, get from operator or command line) set TAPENAME = "unknown" ############### ## Other system dependencies (BSD or SYSV?) alias findproc 'ps ax|grep "\!*"|grep -v grep|sed -e "s/^ *//" -e "s/ .*//"' set UNAME=`uname` switch ( "$UNAME" ) case Linux: set FSTAB = "/etc/fstab" set UMOUNT = "/bin/umount -a -t nfs" set MOUNT = "/bin/mount -a -t nfs" set SMPID = `findproc sendmail` breaksw case HP-UX: set FSTAB = "/etc/mnttab" set UMOUNT = "/usr/sbin/umount -a -F nfs" set MOUNT = "/usr/sbin/mount -a -F nfs" alias findproc 'ps -e|grep "\!*"|grep -v grep|sed -e "s/^ *//" -e "s/ .*//"' # set SMPID = `ps -ef | awk '$9 ~ /sendmail/ {print $2}'` set SMPID = `findproc sendmail` breaksw case NEXTSTEP: set FSTAB = "/etc/fstab" set UMOUNT = "/usr/etc/umount -a -t nfs" set MOUNT = "/usr/etc/mount -a -t nfs" set SMPID = `findproc sendmail` breaksw case ULTRIX: sed -e "s/\:/ /g" /etc/fstab > /tmp/fstab set FSTAB=/tmp/fstab set NOTIFY = "o" # for Non-Ultrix dump set RFLAGS = "o" # for Non-Ultrix restore format set restoreskip # restore can't handle -s flag set UMOUNT = "/bin/umount -a -t nfs" set MOUNT = "/bin/mount -a -t nfs" set SMPID = `ps -acx | awk '$5 ~ /sendmail/ {print $1}'` breaksw case SunOS: set FSTAB = "/etc/fstab" set UMOUNT = "/usr/etc/umount -a -t nfs" set MOUNT = "/usr/etc/mount -a -t nfs" set SMPID = `findproc sendmail` breaksw default: set FSTAB = "/etc/fstab" set UMOUNT = "umount -a -t nfs" set MOUNT = "mount -a -t nfs" set SMPID = `findproc sendmail` endsw ################# ## Compress Table of Contents with compress or gzip? set COMPRESS = "compress -f" if ( -f /usr/bin/gzip ) then set COMPRESS = "/usr/bin/gzip -f" endif if ( -f /usr/local/bin/gzip ) then set COMPRESS = "/usr/local/bin/gzip -f" endif ## Version number for report set RCSVERS = \ `echo '$Revision: 7.4 $' | sed -e 's/Revision: //' -e 's/$\(.*\) \$/\1/'` set RCSDATE = \ `echo '$Date: 2000/06/24 20:06:49 $' | sed -e 's/Date: //' -e 's/$\(.*\) \$/\1/'` ################################ ## Now read the dodump.options file, which can override the defaults above if ( -f ${DUMPDIR}/$PROG.options ) then source ${DUMPDIR}/$PROG.options endif ############################## ## Then override or add to these with command line arguments: while ( $#argv > 0 ) switch ( $1 ) case --help: case -h: # HELP and quit cat <= 0 && ${level} <= 9 ) goto gotlevel #######[The part below not being used very much anymore]######### if ( ! -f ${DUMPDIR}/WHICHDUMP ) then echo "Cannot find dump schedule in WHICHDUMP file." echo "(You can use -L argument to specify a dump level.)" exit 4 endif set which = `head -1 ${DUMPDIR}/WHICHDUMP` @ Ndumps = ${#which} - 1 start: ## if a previous dump did not finish, restart it if ($isnotdone >= 1) then echo "" echo "THIS IS THE CONTINUATION OF AN ABORTED DUMP" echo "Continuing with a level ${which[${which[${#which}]}]} dump." echo " " echo -n "Is this correct? " set answr = $< while ( (${answr} != "yes") && (${answr} != "no") && \ (${answr} != "y") && (${answr} != "n") ) echo -n "Please answer yes or no: " set answr = $< end if ((${answr} == "n") || (${answr} == "no")) then rm -f ${FLAGDIR}/* set isnotdone = 0 goto start endif set restart goto setlevel else # increment the position counter (last element of array) # if the position counter points to its own position in the array, recycle # back to the beginning of the array @ which[$#which]++ if (${which[${#which}]} == ${#which}) set which[${#which}] = 1 recover: echo "" echo -n "My records " if (${?limit}) echo -n "now " echo -n "indicate that this should be a " echo "level ${which[${which[${#which}]}]} dump." echo -n "Is this correct? " set answr = $< while ( (${answr} != "yes") && (${answr} != "no") && \ (${answr} != "y") && (${answr} != "n") ) echo -n "Please answer yes or no: " set answr = $< end echo " " if ((${answr} == "yes") || (${answr} == "y") ) then echo $which > ${DUMPDIR}/WHICHDUMP # setlevel: set level = ${which[${which[${#which}]}]} else # # list the dump sequence and corresponding numbers # ${DUMP} W echo " " echo "The dump schedule listed in WHICHDUMP is:" set Nperline = 4 @ limit = ${#which} set i = 1 while ($i < $limit) echo -n "$which[$i]" if ($i == $which[$#which]) then echo -n "*" endif @ temp = $i - $Nperline * ( $i / $Nperline ) if ($temp == 0) then echo " " else echo -n " " endif @ i++ end echo " " echo "(* marks the dump I thought we were supposed to do.)" echo " " echo -n "What is the number (from 1 to ${Ndumps}) of the correct dump? " set answr = $< while ( (${answr} < "1") || (${answr} >= "${#which}") ) echo "Please choose a number between 1 and ${Ndumps}\!" echo -n "What is the number of the correct dump? " set answr = $< end set which[${#which}] = ${answr} goto recover endif endif #######[The part above not being used very much anymore]######### gotlevel: set lolevel = `echo $level | tr -d "a-z"` ################################################## # Get name of tape, if not known. Print header. while ( ${TAPENAME} == "unknown") echo -n "Enter the name of the backup tape for the level $level dump: " set answr=$< if ( ${answr} != "" ) set TAPENAME = ${answr} echo " " end echo " " echo "${PROG}: `hostname` level $level backup to '${TAPENAME}' file $Nfsf" echo "======= =========================================" echo "(${RCSVERS}) Performed by ${USER} on `date`" echo " " if ( $?eject) then echo " Tape will be ejected when finished\!" echo " " endif ######################################## # DISABLE LOGINS AND SENDMAIL: For low-level dumps turn off sendmail to # keep the filesystem static and to keep from missing mail. onintr interupted # on ^C goto interupted: if ( ${lolevel} <= 1 ) then if ( -f /etc/nologin) mv /etc/nologin /etc/nologin.bak cat >/etc/nologin <Filesystem /${filesystem} " ## look up raw device name in ${FSTAB} if (${filesystem} == "root") then set DEV = `awk '$2 ~ /^\/$/ {print $1}' ${FSTAB}` else set DEV = `echo ${filesystem} | sed -e "s/\//\\\//"` set DEV = `awk '$2 ~ /^\/'${DEV}'$/ { print $1}' ${FSTAB}` endif if ( $DEV == "" ) then echo "not found in ${FSTAB}." echo "This filesystem will not be dumped\! " continue endif echo "is on disk device $DEV." ## Change destination name if it's in a directory, not a device if ( $?dump2disk ) then set Nfsf=0 set DUMPFILE=${MTDEV}/`hostname`-lev${level}.$FLAG set DUMPDEV=${DUMPHOST}:${DUMPFILE} ssh ${DUMPHOST} touch ${DUMPFILE} endif ## get position of dump file on tape, if not set already if ( ${Nfsf} == "" ) set Nfsf = 0 while ( $Nfsf < 0 || $Nfsf > 199 ) echo "Nfsf is the number of tape files to skip before the dump begins." echo "If you put more than one file system on a single tape you must" echo "be sure that there is enough room for both on the same tape" echo "If this is the first or only dump file on this tape simply enter" echo "zero or press RETURN." echo -n "Enter the dump file position on this tape [0]: " set Nfsf = $< if ( ${Nfsf} == "" ) set Nfsf = 0 end ## position the tape if ( $Nfsf > 0) then echo " Positioning tape '${TAPENAME}' on $DUMPDEV to file $Nfsf " ${MTCMD} rewind >>&$LOGFILE ${MTCMD} fsf ${Nfsf} >>&$LOGFILE endif ## do the dump and check that it worked echo " Dumping `hostname`:/${filesystem} to $DUMPDEV file $Nfsf " echo " ${DUMP} ${lolevel}${DFLAGS}dsf ${DEN} ${SIZE} ${DUMPDEV} ${DEV}"\ |& tee -a $LOGFILE ${DUMP} ${lolevel}${DFLAGS}dsf ${DEN} ${SIZE} ${DUMPDEV} ${DEV} \ |& tee -a $LOGFILE set RC=$status # save return code if ( $RC != 0 ) then echo " " echo -n "PANIC\! " echo "${DUMP} FAILED with return code $RC." echo "See the log file $LOGFILE for more info." echo " " goto exit endif echo $Nfsf > ${FLAGFILE1}.${FLAG} # flag this one dumped else echo "Apparently the ${filesystem} dump has been done," set Nfsf=`cat ${FLAGFILE1}.${FLAG}` echo "and it was saved on the tape at position $Nfsf." endif ## do the dump table of contents echo " Doing the dumptoc for ${filesystem} from $DUMPDEV file $Nfsf" if ( ! ( -e ${FLAGFILE2}.${FLAG} )) then # The restore/rrestore command should usually skip forward to the # correct position on the tape itself, by giving it the "s" flag. if ( ! $?restoreskip ) then ${MTCMD} rewind >>&$LOGFILE # make sure we start at begining @ Skip = ${Nfsf} + 1 # restore counts from 1 echo " ${RESTORE} tfs ${DUMPDEV} ${Skip} "\ ">${DUMPDIR}/lev${level}.${FLAG} " |& tee -a $LOGFILE ${RESTORE} tfs ${DUMPDEV} ${Skip} >${DUMPDIR}/lev${level}.${FLAG} \ |& tee -a $LOGFILE set RC=$status # However, Ultrix rrestore is broken, so we have to position the tape # ourselves and then avoid using the "s" flag. else ${MTCMD} rewind >>&$LOGFILE # make sure we start at begining if ( $Nfsf > 0) then echo " Positioning tape to file $Nfsf. . ." ${MTCMD} fsf ${Nfsf} >>&$LOGFILE endif echo " ${RESTORE} t${RFLAGS}f ${DUMPDEV} "\ ">${DUMPDIR}/lev${level}.${FLAG}" |& tee -a $LOGFILE ${RESTORE} t${RFLAGS}f ${DUMPDEV} \ >${DUMPDIR}/lev${level}.${FLAG} |& tee -a $LOGFILE set RC=$status endif if ( $RC != 0 ) then echo " " echo -n "PANIC\! " echo "${RESTORE} FAILED with return code $RC." echo "See the log file $LOGFILE for more info." echo " " goto exit endif if (-z ${DUMPDIR}/lev${level}.${FLAG}) then echo " " echo -n "WARNING\! " echo "Table Of Contents file lev${level}.${FLAG} IS EMPTY\!" echo " " else ${COMPRESS} ${DUMPDIR}/lev${level}.${FLAG} endif ${MTCMD} rewind >>&$LOGFILE # be sure tape is rewound again echo $Nfsf >${FLAGFILE2}.${FLAG} # flag this TOC done ## Record the dump in the DUMPDATES file, get names of additional ## tapes if any were used. echo -n "Level ${level} /${filesystem} " >> ${DATEFILE} echo -n "to tape $TAPENAME file $Nfsf " >> ${DATEFILE} echo -n "on `date` " >> ${DATEFILE} echo "by ${USER}" >> ${DATEFILE} echo " Dump of ${filesystem} on `hostname` is done (file $Nfsf) at `date`." else echo "Well, the dumptoc for ${filesystem} was also already done." endif ## repeat for next file system @ Nfsf++ # next position on the tape echo " " end ######################################## ## Done with all filesystems. Clean up and restore things. ## Eject the tape at end of job, if -j flag given if ( $?eject) ${MTCMD} offline >>&$LOGFILE ## Remove log and flag files clean: rm -f ${FLAGDIR}/* # remove flag files rm -f ${LOGFILE} # remove log file rm -f ${DUMPDIR}/.$PROG.lock # remove in-use flag if ( $UNAME == "ULTRIX" ) rm -f /tmp/fstab echo " Level $level dump on `hostname` finished at `date`." echo " " onintr goto exit ######################################## ## INTERUPTED: interupted: echo "INTERUPT\!" set RC=99 echo " " echo "The dump is being killed. . . . I hope you know what you are doing\!" echo " " onintr ################################ ## ANY EXIT: re-export and re-mount exit: if ( (${lolevel} == "0") || (${lolevel} == "1") ) then ## NFS Exports: echo "Exporting NFS files to clients." echo exportfs -v -a exportfs -v -a ## Automounter: amd or automount? if ( $?AMDPID ) then if ( -x /etc/rc.d/init.d/amd ) then echo "Restarting amd." /etc/rc.d/init.d/amd start else if ( -x /usr/sbin/automount ) then echo "Restarting the automounter." /usr/sbin/automount -f /etc/auto_master endif endif ## NFS Mounts: if ( -x $MOUNT[1] ) then echo "Mounting NFS files from servers." echo $MOUNT $MOUNT else echo "${PROG}: NFS files not re-mounted: Can't find $MOUNT[1]" endif # only restart sendmail if we stopped it earlier if ( "$SMPID" != "") then echo "Restarting sendmail." /usr/lib/sendmail -bd -q1h endif echo "Enabling logins." /bin/rm -f /etc/nologin if ( -f /etc/nologin.bak ) mv /etc/nologin.bak /etc/nologin endif # remove lock file rm -f $DUMPDIR/.$PROG.lock exit $RC