#!/bin/bash # ---------------------------------------------------------------------- # (Based upon, and expanded from, work by Mike Rubel as noted below.) # ---------------------------------------------------------------------- # RCS info: $Id: make_snapshot.sh,v 1.6 2002/04/06 04:20:00 mrubel Exp $ # ---------------------------------------------------------------------- # ============================= Functions ============================ ############################## Debugging Functions ###################### function BUGPAUSE { echo " " echo "-----------------------------------------------------------" echo " MANUAL BUG CHECK!" echo " Is the program doing what you expect? Please " echo " review preceeding output for correctness. Thank you." echo "-----------------------------------------------------------" echo " " ask "Continue? [Y/n]" if [ $? -ne 0 ]; then { exit 100 } fi } #### #### # # Error Handler. # #### #### function ERROR { clear echo "----------------" echo "| ABEND: Code $1 |" echo "-------------------------------------------------------------------------" echo " Bummer! An error occurred!" echo " Don't you just hate it when..." echo "${2}?" echo "==========================" echo " Maybe this will help...." echo "==========================" SYNTAX exit $1 } #### #### # # - SYNTAX -------- Bet you cannot guess what this does. # #### #### function SYNTAX { ask "Do you wish to view the Syntax for SNAPBACK?" if [ $? -ne 0 ]; then return fi echo " " echo "REQUIREMENTS:" echo "This program checks for required software. If the " echo "requirements are not met, the program will exit with a possibly " echo "appropriate message." echo " " echo "Snapback is designed to be run by root, not by the average user." echo "It will check to make sure that root is running it before doing any" echo "backup activity." echo " " echo "SYNTAX: snapback [SERVER] [FILESYSTEM] [NUMBER OF SNAPSHOTS] " echo " Where:" echo " SERVER: The host which we want to back up to local storage." echo " FILESYSTEM: Specifies the top level of the filesystem to back up" echo " NUMBER OF SNAPSHOTS: Specifies the number of snapshots we will retain (default is 5)" echo " " ask "Do you wish to continue?" if [ $? -ne 0 ]; then return fi echo " OPTIONAL 4th PARAMETER MAY BE ONE OF THE FOLLOWING:" echo " " echo " SAMBA: Optional parameter that tells SNAPBACK that the host being backed up is a" echo "Windows machine. This may be abbreviated as -s or -S" echo " " echo " EXCLUSIVE: Optional parameter that tells SNAPBACK to perform a backup of ONLY" echo "the specified filesystem and NOT any filesystems mounted thereunder, e.g. /(root), but" echo "not /home if /home resides on a separate filesystem mounted under /." echo "May be abbreviated as -x or -X." echo " " echo " COMPLETE: Optional parameter that tells SNAPBACK to performa backup of the " echo "specified filesystem AND ALL FILESYSTEMS MOUNTED THEREUNDER (except those specified in" echo "/root/bin/backup_exclude. Any parameter other than SAMBA or EXCLUDE and their" echo "specified variants will be interpreted as selecting COMPLETE. Omission of this " echo "optional parameter will ALSO be interpreted as selecting \"COMPLETE\"." echo "filesystems to be backed up from the Windows server in /mnt/servername." echo " " ask "Do you wish to continue?" if [ $? -ne 0 ]; then return fi echo "With the optional SAMBA parameter: SAMBA, Abbreviated as -s or -S" echo " " echo "In order to back up Windows machines, it is necessary to mount the" echo "filesystems to be backed up from the Windows server in /mnt/servername." echo "using appropriate smbclient commands or entries like these in /etc/fstab:" echo "(You may need to widen your screen to see the following commands in their full, " echo "un-line-wrapped glory.)" echo '//lacaille/c-lacaille /mnt/lacaille/c-lacaille smbfs credentials=/etc/samba/auth.lacaille.vmorgo 0 0' echo '//lacaille/d-lacaille /mnt/lacaille/d-lacaille smbfs credentials=/etc/samba/auth.lacaille.vmorgo 0 0' echo " " echo "NOTES:" echo "Support for backing up Windows shares SAMBA and smbclient software be installed." echo " " echo "In the example above, we are actually mounting TWO shares from the" echo "Windows server Lacaille, since Lacaille has both a C drive and a D drive." echo " " echo "When working with SAMBA, this program expects the backup targets to be in" echo "/mnt/servername/filesytem-to-backup, though that mount may be (and should be) read-only." echo " " echo "If you share the backup directories to the network you should make the shares" echo "read-only, so that the average user may easily restore files by copying, but cannot" echo "accidentally (or intentionally) delete the source files from the backup directories." echo " " ask "Do you wish to continue?" if [ $? -ne 0 ]; then return fi echo "With the optional EXCLUSIVE parameter: EXCLUSIVE, Abbreviated as -x or -X" echo " " echo "This is for backing up UNIX hosts and Linux hosts. In this mode, Snapback will" echo "back up ONLY the specified filesystem. It will not try to back up filesystems" echo "mounted under the target filesystem, e.g. if /(root) is specified, it will back " echo "that up, and everything under it, but would skip /home if /home was mounted on a " echo "separate filesystem (e.g. disk). See /etc/fstab to see what filesystems are mounted where." echo " " echo "Again, of course, it is suggested that the snapback backup directories be" echo "shared read-only so that users might mount them and manually copy a file from" echo "the backup directory(ies) should such a need arise." echo "Unix, Mac OS X, etc." echo " " ask "Do you wish to continue?" if [ $? -ne 0 ]; then return fi echo "With the optional COMPLETE parameter: COMPLETE (may be omitted or any text string)" echo "This is the DEFAULT mode of operation." echo " " echo "In its default mode of operation, Snapback expects to be doing an RSYNC-type" echo "backup of remote UNIX/LINUX machines." echo "It will back up the target filesystem and all filesystems mounted under the " echo "target filesystem except those specified in /root/bin/backup_exclude." echo " " ask "Do you wish to continue?" if [ $? -ne 0 ]; then return fi echo "NOTES:" echo "How filesystems are mounted, e.g. nfs, smbfs, local, etc. does not matter." echo "If the filesystem can be read through your chosen mounting mechanism" echo "Snapback will attempt to read it." echo " " echo "WARNINGS!" echo "Do NOT attempt to use smbfs to back up *NIX filesystems. SMBFS does not support" echo "symbolic links, while most UNICES do. That means that when Snapback tries to " echo "back up such a filesystem mounted through smbfs, it will end up dereferencing all " echo "symbolic links to the files and directories they point to." echo "As you can well imagine, using smbfs to back up a *NIX filesystem containing" echo "circular symlinks, e.g. symlink a points to directory b, which" echo "contains directory c, which includes a symlink pointing to directory b, which contains" echo "directory c, which includes a symlink pointing to directory b...." echo " " echo "Do not attempt to use the DEFAULT setting ("COMPLETE") on a server hosting " echo "network mounts unless you want those network mounted filesystems backed up" echo "(or have excluded them in /root/bin/backup_exclude.)" echo " " echo "**** End of help. Don't you wish there was more? ****" return } #### #### # # - CHECKCMD --------- Locates the system cammands used by this script.------- # This makes sure the commands we need exist. If a command is missing, # this function will cause the script to abort. # #### #### function CHECKCMD { echo "---->In CHECKCMD...." for i in id seq echo mount smbmount rm mv cp touch hostname rsync; do which $i; if [ $? -ne 0 ]; then ERROR 1 "the command $i is missing from the system" fi; done if [ -f /root/bin/backup_exclude ]; then echo '/root/bin/backup_exclude found. Proceeding.' else ERROR 2 'when the /root/bin/backup_exclude file is missing' fi if [ -f /root/bin/snapback_hostkey ]; then echo '/root/bin/snapback_hostkey found. Proceeding.' else ERROR 3 'when the /root/bin/snapback_hostkey file is missing' fi } #### #### # # - R_U_ROOT ----- Aborts the program if the person running it is not root. # #### #### function R_U_ROOT { ID=`id -u` if [ $ID -ne 0 ]; then { echo "Snapback is a potentially dangerous program." echo "Therefore it is only to be run by the root user." echo "You appear to be running it as `id -nu`." echo "Please try again as root or have the system administrator run" echo "SNAPBACK for you." ERROR 10 "you forget to log in as root first" } fi } #### #### # # CHECK_HOST ----Aborts the program if the hostname keyfile is missing. # #### #### function CHECK_HOST() { AUTHORIZED_BACKUP_HOST=`cat /root/bin/snapback_hostkey` if [ $AUTHORIZED_BACKUP_HOST = `hostname` ]; then { echo "Host $AUTHORIZED_BACKUP_HOST validated." } else { ERROR 20 "When you try to run Snapback on an unauthorized backup host" } fi } #### #### # # - GETARGS ----------- Converts command-line args to appropriate parameters. Otherwise, # it prompts for the necessary arguments. # #### #### function GETARGS { echo "IN GETARGS...." if [ -z $1 ]; then { echo "What server are we backing up?" read SERVER if [ -z $SERVER ]; then ERROR 30 "you forget to specify a server to back up" fi } else SERVER=$1 fi if [ -z $2 ]; then { echo "What filesystem are we backing up on $SERVER?" read FILESYSTEM if [ -z $FILESYSTEM ]; then ERROR 31 "you forget to specify a filesystem to back up" fi } else FILESYSTEM=$2 fi if [ -z $3 ]; then { echo "How far back should we go in terms of snapshot history?" read HISTORY if [ -z $HISTORY ]; then HISTORY=5 echo "We will set up for a history of 5 snapshots." fi } else { HISTORY=$3 } fi if [ -z $4 ]; then { echo "What kind of backup is this?" echo "Choices are" echo "1. Back up everything under a Windows filesystem through Samba" echo "$SERVER must be a Windows box, of course, for this to work." echo " " echo "2. Back up ONLY the $FILESYSTEM on $SERVER, but not any of the file-" echo "systems mounted under $FILESYSTEM or elsewhere on $SERVER" echo " " echo "3. Back up $SERVER:$FILESYSTEM and every filesystem thereunder." read KIND case "$KIND" in 1) { KIND="SAMBA" echo "We will do a $KIND snapback of the Windows $FILESYSTEM." } ;; 2) { KIND="EXCLUSIVE" echo "We will do a(n) $KIND snapback of only the $FILESYSTEM through SSH." } ;; *) { echo "We will do a Standard snapback of $FILESYSTEM through SSH and all" echo "filesystems mounted thereunder.(!)." KIND="COMPLETE" } ;; esac } else { KIND=$4 } fi echo "I am backing up $FILESYSTEM on $SERVER." echo "I am going to retain a history of $HISTORY snapshots as of this run." echo "This backup is of type: $KIND" echo "NOTE: If the type is \"COMPLETE\", blank or anything other than SAMBA/-s/-S or EXCLUSIVE/-x/-X, a " echo "COMPLETE backup of $FILESYSTEM will be taken from $SERVER through SSH. It will INCLUDE" echo "all filesystems mounted under or within $FILESYSTEM. Symlinks WILL be followed." sleep 3 } #### #### # # Reports the version for this program. # #### #### function VERSION { echo "SNAPBACK Version 02152k4-a" echo "RSYNC-based backup utility based upon work by" echo " -------------------------------------------------------------------" echo " (Based upon, and expanded from works by Mike Rubel as noted below.)" echo " -------------------------------------------------------------------" echo " make_snapshot.sh,v 1.6 2002/04/06 04:20:00 mrubel" echo " -------------------------------------------------------------------" echo " " } #======================================= No more functions. ========== ############################################################################# ################################# #################################### ################################# MAIN #################################### ################################# #################################### ############################################################################# case "$1" in v|V|-v|-V|--version|--VERSION|--ver) VERSION exit 0 ;; h|H|-h|-H|--help|--HELP|HELP|help|Help|--Help) SYNTAX exit 0 ;; esac BACKUP_ROOT=/usr2/backup CHECK_HOST R_U_ROOT VERSION CHECKCMD GETARGS $1 $2 $3 $4 #---------------------------------------------------------------------- #TEST-SWITCHES #Comment out for normal use. #SERVER=stjoseph #FILESYSTEM=d-stjoseph # ------------- file locations ----------------------------------------- echo "IN MAIN-------->>>>>>>" echo "SNAPSHOT_RW=$BACKUP_ROOT/$SERVER/$FILESYSTEM and HISTORY=$HISTORY days." SNAPSHOT_RW=$BACKUP_ROOT/$SERVER/$FILESYSTEM #EXCLUDES=/usr/local/etc/backup_exclude; #EDIT THIS if you want the excludes file to be somewhere else. EXCLUDES=/root/bin/backup_exclude # ------------- Make target directory if it doesn't exist. ------------- if [ -z $SNAPSHOT_RW ]; then mkdir -p $SNAPSHOT_RW echo "Made the subdirectory." fi # ------------- the script itself -------------------------------------- # step 1: delete the oldest snapshot, if it exists: if [ -d $SNAPSHOT_RW/daily.$HISTORY ] ; then echo $SNAPSHOT_RW rm -rf $SNAPSHOT_RW/daily.$HISTORY ; fi echo "New Improved Step 2: Shift the snapshots back by one, if they exist." LAST=`expr $HISTORY - 2` for i in `seq 0 1 $LAST`; do OLDEST=`expr $HISTORY - $i` MIDDLE=`expr $OLDEST - 1` if [ -d $SNAPSHOT_RW/daily.$MIDDLE ]; then echo "Moving $SNAPSHOT_RW/daily.$MIDDLE $SNAPSHOT_RW/daily.$OLDEST". mv $SNAPSHOT_RW/daily.$MIDDLE $SNAPSHOT_RW/daily.$OLDEST else echo "$SNAPSHOT_RW/daily.$MIDDLE does not (yet) exist." fi done echo "step 3: make a hard-link-only (except for dirs) copy of the latest snapshot, if that exists" if [ -d $SNAPSHOT_RW/daily.0 ] ; then cp -al $SNAPSHOT_RW/daily.0 $SNAPSHOT_RW/daily.1 ; else { echo "$SNAPSHOT_RW/daily.0 does not seem to be a directory?" echo 'So, I think I will make it one.' if [ -f $SNAPSHOT_RW/daily.0 ] ; then rm -f $SNAPSHOT_RW/daily.0 fi mkdir -p $SNAPSHOT_RW/daily.0 } fi; echo "Step 4: RSYNC from $SERVER $FILESYSTEM into $SNAPSHOT_RW/daily.0" # step 4: rsync from the system into the latest snapshot (notice that # rsync behaves like cp --remove-destination by default, so the destination # is unlinked first. If it were not so, this would copy over the other # snapshot(s) too! mkdir -p $SNAPSHOT_RW case "$KIND" in samba|SAMBA|Samba|s|S|-s|-S|--samba|--SAMBA|--Samba|windows|WIN|win|Win|WINDOWS|Windows|--Windows|--WINDOWS|--windows|-w|-W|w|W) { echo "------> Snapback is using SAMBA backup of <----- " echo "------> $FILESYSTEM on $SERVER <-----" echo "------> AND EVERY FILESYSTEM THEREUNDER <------" echo "------> There better not be any symlinks! <-----" rsync \ -av --delete --delete-excluded --modify-window=1 \ --exclude-from="$EXCLUDES" --safe-links $5 \ /mnt/$SERVER/$FILESYSTEM $SNAPSHOT_RW/daily.0 ; } ;; x|X|-x|-X|--Exclusive|--EXCLUSIVE|--exclusive|EXCLUSIVE|Exclusive|exclusive|exclude|EXCLUDE|Exclude|-Exclusive|-exclusive|-EXCLUSIVE|-e|-E) { echo "****** Performing EXCLUSIVE Snapshot on ONE FILESYSTEM ONLY through SSH ******" echo "****** Will not back up filesystems other than $FILESYSTEM! ******" echo "ARGS-->:$SERVER:$FILESYSTEM:$SNAPSHOT_RW/daily.0:" rsync \ -avx --delete --delete-excluded -e ssh \ --exclude-from="$EXCLUDES" \ $5 $SERVER:$FILESYSTEM \ $SNAPSHOT_RW/daily.0 } ;; *) { echo "+++++ Performing DEFAULT standard Snapshot through SSH +++++" echo "+++++ This will attempt to back up $FILESYSTEM +++++" echo "+++++ AND EVERY FILESYSTEM MOUNTED THEREUNDER !!! (BE CAREFUL!) +++++" rsync \ -av --delete --delete-excluded -e ssh \ --exclude-from="$EXCLUDES" \ $5 $SERVER:$FILESYSTEM \ $SNAPSHOT_RW/daily.0 } ;; esac # step 5: update the mtime of daily.0 to reflect the snapshot time touch $SNAPSHOT_RW/daily.0 ;