#!/bin/bash # rescuebru.sh # Rescue CD Backup & Restore Utility client script # version 7.2.5 # Rod Wright 1/19/2024 # Refer to CHANGELOG file for details. # ----------------------------------------------------------------------------- PROG_NAME="rescuebru.sh" PROG_VERSION=7.2.5 # Paths RBRU_INSTALL_PATH="/root/rescuebru" MEDIA_MNT_DIR="/mnt/rescuebru" BACKUP_DIR="rescuebru-backups" IMAGE_PATH="$MEDIA_MNT_DIR/$BACKUP_DIR" HELP_PATH="$RBRU_INSTALL_PATH/rescuebru-help" NFS_REMOTE_DIR_DEF="/mnt/rescuebru" LOCAL_DIR="/mnt/rescuebru/rescuebru-backups" LOG_PATH="/root/rescuebru_logs" # Arguments for external programs PI_BACKUP_ARGS="-cod --debug=1 -M -B\"a=b\" save" PI_RESTORE_ARGS="-B\"a=b\" --debug=1 restore" FA_BACKUP_ARGS="savefs -s 2000 -v -d -j4" FA_RESTORE_ARGS="restfs -v -d -j4" SFD_RESTORE_ARGS="-q --force --no-reread" for subnet in `seq 10`; do wlist="$wlist -W 10.111.$subnet.1";done DHCPCD_RBRUSERVER_ARGS="-d -L -t120 -h rescuebru `echo $wlist`" DHCPCD_GLOBAL_ARGS="-d -L -t120 -h rescuebru" MAX_CARRIER_ATTEMPTS=10 # Defaults MEDIA_TYPE_DEF="NFS" BU_METHOD_DEF="auto" NFS_SUBNET_DEF="10.111.1" NFS_REMOTE_ADDR_DEF="$NFS_SUBNET_DEF.1" NFS_LOCAL_DEF="99" NFS_CLIENT_START_DEF="20" NETSETUP_TYPE="auto" BACKUP_SERVER_TYPE="RescueBRU Server" POSTACTION_DEF="Return to menu" BU_COMP_DEF="normal" # Initial status CLIENT_NET_STATUS=0 LOCAL_STATUS=0 # Values ROUTE_TIMEOUT=30 MIN_MEDIA_FREE=2048 NOTES_TEMPLATE="$MEDIA_MNT_DIR/notes_template.txt" TEMP_NOTES="/root/${operation}_`date +%Y-%m-%d_%H%M%S`.notes" # Logging LOGGING_ENABLED=1 SESSION_LOG="$LOG_PATH/session_`date +%Y-%m-%d_%H%M%S`.log" OPERATION_LOG="/dev/null" # There is no operation yet, but the text # needs to have somewhere to go. This # will get redefined as a real file at the # beginning of a real operation. Set it back # to /dev/null at the end of a real # operation so it won't continue to # accumulate data from subsequent operations. # Import functions source $RBRU_INSTALL_PATH/functions.sh # Declare global associative arrays declare -A blockdevs declare -A seldevs declare -A devbm declare -A images declare -A selimages declare -A imgid # ----------- BACKUP/RESTORE FUNCTIONS ------------- function do_backup() { # Handles backup process operation="backup" operation_log "on" unset imagename srcimagename destimagename iname_confirmed iname_invalid unset FAIL prabrt for key in ${!blockdevs[@]}; do unset blockdevs[$key]; done for key in ${!seldevs[@]}; do unset seldevs[$key]; done for key in ${!devbm[@]}; do unset devbm[$key]; done progreport "" log "Starting $operation process." # Attempt to mount media media_reqd=1 if ! media "mount" ; then pause "Unable to mount backup media." log "Unable to mount backup media." operation_log "off" rm -f $LOG_PATH/backup*.log return 1 fi if [ "$CLIENT_NET_STATUS" -eq 1 ]; then echo "is allocated and is performing a backup.">$MEDIA_MNT_DIR/$wkg_addr fi # Check that media is set up if [ ! -d $IMAGE_PATH ]; then inform "Backup media is not set up." if ! mediasetup ; then pause "Backup media could not be set up. Aborting." log "Backup media could not be set up. Aborting." operation_log "off" rm -f $LOG_PATH/backup*.log return 1 fi if [ "$CLIENT_NET_STATUS" -eq 1 ]; then echo "is allocated and is performing a backup.">$MEDIA_MNT_DIR/$wkg_addr fi fi # Get image name if ! getimagename "backup" ; then pause "User aborted while getting image name." log "User aborted while getting image name." if [ "$CLIENT_NET_STATUS" -eq 1 ]; then echo "is allocated and idle.">$MEDIA_MNT_DIR/$wkg_addr fi media "unmount" operation_log "off" rm -f $LOG_PATH/backup*.log return 0 fi if [ "$CLIENT_NET_STATUS" -eq 1 ]; then echo "is allocated and is performing a backup of $imagename.">$MEDIA_MNT_DIR/$wkg_addr fi # Get info on all disks log "Getting disk information..." inform "Getting disk information..." "2" if getblkdevs ; then log "Got disk information." else pause "Couldn't get disk information." log "Couldn't get disk information." if [ "$CLIENT_NET_STATUS" -eq 1 ]; then echo "is allocated and idle.">$MEDIA_MNT_DIR/$wkg_addr fi media "unmount" operation_log "off" rm -f $LOG_PATH/backup*.log return 1 fi # Select devices to be backed up log "Selecting device(s) to back up..." inform "Selecting device(s) to back up..." "2" if deviceselect "multiple" "Select devices to back up"; then log "Selected device(s) to back up." else pause "Couldn't select device(s) to back up." log "Couldn't select device(s) to back up." if [ "$CLIENT_NET_STATUS" -eq 1 ]; then echo "is allocated and idle.">$MEDIA_MNT_DIR/$wkg_addr fi media "unmount" operation_log "off" rm -f $LOG_PATH/backup*.log return 0 fi # Analyze and determine backup method if set to auto if [[ $bu_method = "auto" ]]; then inform "Determining backup method." log "Determining backup method." method_analyze fi # Get notes for backup log "Getting notes for $operation." if [ -e $NOTES_TEMPLATE ]; then cat $NOTES_TEMPLATE>$TEMP_NOTES else touch $TEMP_NOTES fi get_notes "$operation of $imagename" # Get backup version log "Getting backup version..." get_version "$operation of $imagename" log "Backup version is $bu_version" # Set compression level arguments setcomplevel # Confirm actions log "Confirming actions..." if confirm_actions ; then inform "Backup actions confirmed." "2" log "Backup actions confirmed." # Write the status file # Create the directory if createdir ; then log "Backup directory created." else pause "Backup directory could not be created." log "Backup directory could not be created." if [ "$CLIENT_NET_STATUS" -eq 1 ]; then echo "is allocated and idle.">$MEDIA_MNT_DIR/$wkg_addr fi media "unmount" operation_log "off" rm -f $LOG_PATH/backup*.log return 1 fi # Write version number file if echo "$bu_version">$IMAGE_PATH/$imagename/version.txt ; then inform "Wrote version number file." "2" log "Wrote version number file." else inform "WARNING: Couldn't write version number file." log "WARNING: Couldn't write version number file." fi # Write backup date file if date +%Y-%m-%d >$IMAGE_PATH/$imagename/latest_backup_date.txt ; then inform "Wrote backup date file." "2" log "Wrote backup date file." else inform "WARNING: Couldn't write backup date file." log "WARNING: Couldn't write backup date file." fi # Run backups of selected devices for budev in `echo ${!seldevs[@]}|tr " " "\n"|sort|tr "\n" " "` ; do # Determine device type if echo $budev|egrep -q -o -e "sd[a-z]$|c[0-9]d[0-9]$|hd[a-z]$|nvme[0-9]n[0-9]$" ; then # device is a drive backup="drivebackup" elif echo $budev|egrep -q -o -e "sd[a-z][1-9]$|c[0-9]d[0-9]p[0-9]$|hd[a-z][1-9]$|nvme[0-9]n[0-9]p[0-9]$" ; then # device is a partition backup="partbackup" else pause "$FUNCNAME : $LINENO :Internal program error! Unable to determine devtype!" FAIL=1 if [ "$CLIENT_NET_STATUS" -eq 1 ]; then echo "is allocated and idle.">$MEDIA_MNT_DIR/$wkg_addr fi media "unmount" operation_log "off" rm -f $LOG_PATH/backup*.log return 1 fi id="${seldevs[$budev]}" log "Starting backup of $budev." inform "Starting backup of $budev." "2" if $backup $budev "$id"; then log "Backup of $budev succeeded." inform "Backup of $budev succeeded." "2" else inform "Errors were encountered while backing up $budev." log "Errors were encountered while backing up $budev." FAIL=1 break fi done else pause "$operation aborted by user." log "$operation aborted." if [ "$CLIENT_NET_STATUS" -eq 1 ]; then echo "is allocated and idle.">$MEDIA_MNT_DIR/$wkg_addr fi if [ $media_reqd ]; then media "unmount"; fi operation_log "off" rm -f $LOG_PATH/backup*.log return 0 fi if [ ! "$FAIL" ]; then inform "Backup finished." log "Backup finished." if [ $media_reqd ]; then if [ $imgovr ]; then log "Replacing $finalimgname with $imagename." inform "Replacing $finalimgname with $imagename. Please wait..." rm -rf $IMAGE_PATH/$finalimgname mv $IMAGE_PATH/$imagename $IMAGE_PATH/$finalimgname inform "Replacing $finalimgname with $imagename. Please wait... Done" else finalimgname=$imagename fi fi if [ "$postaction" = "Reboot" ]; then log "Backup finished. Starting automatic reboot..." inform "Backup finished. Starting automatic reboot..." if [ $media_reqd ]; then cp $LOG_PATH/*.log $IMAGE_PATH/$finalimgname/ cp $TEMP_NOTES $IMAGE_PATH/$finalimgname/backup_`date +%Y-%m-%d_%H%M%S`.notes rm -f $TEMP_NOTES if [ "$CLIENT_NET_STATUS" -eq 1 ]; then echo "is allocated and idle.">$MEDIA_MNT_DIR/$wkg_addr fi media "unmount" fi if ! shutdown_proc "reboot" ; then if [ $media_reqd ]; then media "mount"; fi pause "Reboot aborted." log "Reboot aborted." let prabrt=1 fi fi if [ "$postaction" = "Shut Down" ]; then log "Backup finished. Starting automatic shutdown..." inform "Backup finished. Starting automatic shutdown..." if [ $media_reqd ]; then cp $LOG_PATH/*.log $IMAGE_PATH/$finalimgname/ cp $TEMP_NOTES $IMAGE_PATH/$finalimgname/backup_`date +%Y-%m-%d_%H%M%S`.notes rm -f $TEMP_NOTES if [ "$CLIENT_NET_STATUS" -eq 1 ]; then echo "is allocated and idle.">$MEDIA_MNT_DIR/$wkg_addr fi media "unmount" fi if ! shutdown_proc "shutdown" ; then if [ $media_reqd ]; then media "mount";fi pause "Powerdown aborted." log "Powerdown aborted." let prabrt=1 fi fi dialog --textbox $OPERATION_LOG 24 80 if [ ! $prabrt ]; then if [ $media_reqd ]; then cp $LOG_PATH/*.log $IMAGE_PATH/$finalimgname/ cp $TEMP_NOTES $IMAGE_PATH/$finalimgname/backup_`date +%Y-%m-%d_%H%M%S`.notes rm -f $TEMP_NOTES if [ "$CLIENT_NET_STATUS" -eq 1 ]; then echo "is allocated and idle.">$MEDIA_MNT_DIR/$wkg_addr fi media "unmount" fi fi else inform "Backup FAILED!" log "Backup FAILED!" if [ $imgovr ]; then inform "NOT replacing original backup." log "NOT replacing original backup." fi if [ -s "$OPERATION_LOG" ]; then dialog --title "Backup Log" --textbox $OPERATION_LOG 24 80 ;fi if [ $media_reqd ]; then cp $LOG_PATH/*.log $IMAGE_PATH/$imagename/ cp $TEMP_NOTES $IMAGE_PATH/$imagename/`date +%Y-%m-%d_%H%M%S`backup.notes if approved "Would you like to keep the failed backup?" "y"; then inform "Saving failed backup as $imagename.fail. Please wait..." mv $IMAGE_PATH/$imagename $IMAGE_PATH/$imagename.fail inform "Saving failed backup as $imagename.fail. Please wait... done." else inform "Removing $imagename. Please wait..." rm -rf $IMAGE_PATH/$imagename inform "Removing $imagename. Please wait... done." fi if [ "$CLIENT_NET_STATUS" -eq 1 ]; then echo "is allocated and idle.">$MEDIA_MNT_DIR/$wkg_addr fi media "unmount" fi fi operation_log "off" rm -f $LOG_PATH/backup*.log return 0 } function do_restore() { # Handles restoration process operation="restore" operation_log "on" unset imagename srcimagename destimagename iname_confirmed iname_invalid unset FAIL prabrt for key in ${!images[@]}; do unset images[$key]; done for key in ${!selimages[@]}; do unset selimages[$key]; done for key in ${!imgid[@]}; do unset imgid[$key]; done log "Starting $operation process." # Attempt to mount media media_reqd=1 if ! media "mount" ; then pause "Unable to mount backup media." log "Unable to mount backup media." operation_log "off" rm -f $LOG_PATH/restore*.log return 1 fi if [ "$CLIENT_NET_STATUS" -eq 1 ]; then echo "is allocated and is performing a restore.">$MEDIA_MNT_DIR/$wkg_addr fi # Get image name if ! getimagename "restore" ; then pause "User aborted while getting image name." log "User aborted while getting image name." if [ "$CLIENT_NET_STATUS" -eq 1 ]; then echo "is allocated and idle.">$MEDIA_MNT_DIR/$wkg_addr fi media "unmount" operation_log "off" rm -f $LOG_PATH/restore*.log return 1 fi if [ "$CLIENT_NET_STATUS" -eq 1 ]; then echo "is allocated and is performing a restore of $imagename.">$MEDIA_MNT_DIR/$wkg_addr fi # Get info on all disks log "Getting disk information..." inform "Getting disk information..." "2" if getblkdevs ; then log "Got disk information on block devices" else pause "Couldn't get disk information." log "Couldn't get disk information." if [ "$CLIENT_NET_STATUS" -eq 1 ]; then echo "is allocated and idle.">$MEDIA_MNT_DIR/$wkg_addr fi media "unmount" operation_log "off" rm -f $LOG_PATH/restore*.log return 1 fi # Build an array of images log "Getting saved images..." if getimages ; then log "Got saved images." else pause "Couldn't get saved images." log "Couldn't get saved images." if [ "$CLIENT_NET_STATUS" -eq 1 ]; then echo "is allocated and idle.">$MEDIA_MNT_DIR/$wkg_addr fi media "unmount" operation_log "off" rm -f $LOG_PATH/restore*.log return 1 fi # Select images to be restored log "Selecting image(s) to be restored..." sleep 2 if imageselect ; then sleep 3 log "Selected image(s) to restore." else pause "Couldn't select image(s) to restore." log "Couldn't select image(s) to restore." if [ "$CLIENT_NET_STATUS" -eq 1 ]; then echo "is allocated and idle.">$MEDIA_MNT_DIR/$wkg_addr fi media "unmount" operation_log "off" rm -f $LOG_PATH/restore*.log return 1 fi # Analyze and warn about images too big for drive for aimg in ${!selimages[@]} ; do # get the size of the device that was backed up if busize=$(cat $IMAGE_PATH/$imagename/`echo "$aimg"|rev|cut -d"/" -f1|rev`.sz) ; then # if the backup is a partition, get drive name if echo "$aimg"|egrep -q -o -e "sd[a-z][1-9]$|c[0-9]d[0-9]p[0-9]$|hd[a-z][1-9]$|nvme[0-9]n[0-9]p[0-9]$" ; then if echo "$aimg"|egrep -q -o -e "c[0-9]+d[0-9]+p[0-9]+|nvme[0-9]+n[0-9]+p[0-9]+" ; then local drive=`echo $aimg|rev|cut -c3-|rev` else local drive=`echo $aimg|rev|cut -c2-|rev` fi else local drive="$aimg" fi if contains "$drive" ${!blockdevs[@]} ; then devsize=`blockdev --getsize64 $drive 2>/dev/null` if [ $busize -gt $devsize ]; then if ! approved "The device this image was made for was larger than the current disk. It probably will fail to restore correctly. Proceed anyway?" "n"; then inform "Restore aborted." log "Restore aborted." if [ "$CLIENT_NET_STATUS" -eq 1 ]; then echo "is allocated and idle.">$MEDIA_MNT_DIR/$wkg_addr fi media "unmount" operation_log "off" rm -f $LOG_PATH/restore*.log return 1 fi fi else pause "The disk this backup was created from no longer exists. Restore aborted." if [ "$CLIENT_NET_STATUS" -eq 1 ]; then echo "is allocated and idle.">$MEDIA_MNT_DIR/$wkg_addr fi media "unmount" operation_log "off" rm -f $LOG_PATH/restore*.log return 1 fi else log "Unable to determine size of the original drive." inform "Unable to determine size of the original drive." fi done # Get notes for restore log "Getting notes for $operation." get_notes "$operation of $imagename" # Confirm actions log "Confirming actions..." if confirm_actions ; then inform "Restore actions confirmed." "2" log "Restore actions confirmed." log "Backup method was $bu_method" # Restore selected images for devimg in `echo ${!selimages[@]}|tr " " "\n"|sort|tr "\n" " "` ; do # Determine device type if echo $devimg|egrep -q -o -e "sd[a-z]$|c[0-9]d[0-9]$|hd[a-z]$|nvme[0-9]n[0-9]$" ; then # device is a drive restore="driverestore" elif echo $devimg|egrep -q -o -e "sd[a-z][1-9]$|c[0-9]d[0-9]p[0-9]$|hd[a-z][1-9]$|nvme[0-9]n[0-9]p[0-9]$" ; then # device is a partition restore="partrestore" else pause "$FUNCNAME : $LINENO :Internal program error! Unable to determine devtype!" FAIL=1 operation_log "off" rm -f $LOG_PATH/restore*.log return 1 fi method="${selimages[$devimg]}" log "Starting restore of $devimg ." inform "Starting restore of $devimg." "2" if $restore $devimg $method; then inform "Restore of $devimg succeeded." "2" log "Restore of $devimg succeeded." else inform "Errors were encountered while restoring $devimg ." log "Errors were encountered while restoring $devimg." FAIL=1 break fi done else pause "$operation aborted by user." log "$operation aborted." if [ "$CLIENT_NET_STATUS" -eq 1 ]; then echo "is allocated and idle.">$MEDIA_MNT_DIR/$wkg_addr fi if [ $media_reqd ]; then media "unmount"; fi operation_log "off" rm -f $LOG_PATH/restore*.log return 0 fi if [ ! "$FAIL" ]; then if [ "$postaction" = "Reboot" ]; then log "Restore finished. Starting automatic reboot..." inform "Restore finished. Starting automatic reboot..." if [ $media_reqd ]; then cp $LOG_PATH/*.log $IMAGE_PATH/$imagename/ cp $TEMP_NOTES $IMAGE_PATH/$imagename/restore_`date +%Y-%m-%d_%H%M%S`.notes rm -f $TEMP_NOTES if [ "$CLIENT_NET_STATUS" -eq 1 ]; then echo "is allocated and idle.">$MEDIA_MNT_DIR/$wkg_addr fi media "unmount" fi if ! shutdown_proc "reboot" ; then if [ $media_reqd ]; then media "mount"; fi pause "Reboot aborted." log "Reboot aborted." let prabrt=1 fi fi if [ "$postaction" = "Shut Down" ]; then log "Restore finished. Starting automatic shutdown..." inform "Restore finished. Starting automatic shutdown..." if [ $media_reqd ]; then cp $LOG_PATH/*.log $IMAGE_PATH/$imagename/ cp $TEMP_NOTES $IMAGE_PATH/$imagename/restore_`date +%Y-%m-%d_%H%M%S`.notes rm -f $TEMP_NOTES if [ "$CLIENT_NET_STATUS" -eq 1 ]; then echo "is allocated and idle.">$MEDIA_MNT_DIR/$wkg_addr fi media "unmount" fi if ! shutdown_proc "shutdown" ; then if [ $media_reqd ]; then media "mount";fi pause "Powerdown aborted." log "Powerdown aborted." let prabrt=1 fi fi if [ -s "$OPERATION_LOG" ]; then dialog --title "Restore Log" --textbox $OPERATION_LOG 24 80 ;fi if [ ! $prabrt ]; then if [ $media_reqd ]; then cp $LOG_PATH/*.log $IMAGE_PATH/$imagename/ cp $TEMP_NOTES $IMAGE_PATH/$imagename/restore_`date +%Y-%m-%d_%H%M%S`.notes rm -f $TEMP_NOTES if [ "$CLIENT_NET_STATUS" -eq 1 ]; then echo "is allocated and idle.">$MEDIA_MNT_DIR/$wkg_addr fi media "unmount" fi fi else inform "Restore FAILED!" log "Restore FAILED!" if [ -s "$OPERATION_LOG" ]; then dialog --title "Restore Log" --textbox $OPERATION_LOG 24 80 ;fi if [ $media_reqd ]; then cp $LOG_PATH/*.log $IMAGE_PATH/$imagename/ cp $TEMP_NOTES $IMAGE_PATH/$imagename/restore_`date +%Y-%m-%d_%H%M%S`.notes if [ "$CLIENT_NET_STATUS" -eq 1 ]; then echo "is allocated and idle.">$MEDIA_MNT_DIR/$wkg_addr fi media "unmount" fi fi operation_log "off" rm -f $LOG_PATH/restore*.log return 0 } function do_image_mod() { # Sets up for image management processes unset imagename srcimagename destimagename iname_confirmed iname_invalid # Attempt to mount media media_reqd=1 if ! media "mount" ; then pause "Unable to mount backup media." log "Unable to mount backup media." return 1 fi if [ "$CLIENT_NET_STATUS" -eq 1 ]; then echo "is allocated and is modifying images.">$MEDIA_MNT_DIR/$wkg_addr fi image_mod if [ "$CLIENT_NET_STATUS" -eq 1 ]; then echo "is allocated and idle.">$MEDIA_MNT_DIR/$wkg_addr fi media "unmount" return 0 } function get_notes() { # Prompt user to enter notes to be saved with a backup or restore. # Takes one argument, the title text of the dialog box notetexttmp=`mktemp -q` if [ $imgovr ]; then if lastbkupnotes=`ls $IMAGE_PATH/$imagename/backup*notes|sort|tail -1 2>/dev/null` ; then inform "Importing previous backup notes" cp -f $lastbkupnotes $TEMP_NOTES fi fi touch $TEMP_NOTES if dialog --title "$1" \ --backtitle "Enter notes or leave blank then Tab to or and press enter." \ --ok-label "Submit" --cancel-label "No notes" \ --editbox $TEMP_NOTES 20 80 2>$notetexttmp ; then cp -f $notetexttmp $TEMP_NOTES else rm -f $TEMP_NOTES fi rm -f $notetexttmp } function get_version() { # Prompt user to enter version number to be saved with a backup. # Takes one argument, the title text of the dialog box. # Creates a string variable, bu_version, containing version text local verstext_invalid=1 while [ $verstext_invalid ]; do verstmp=`mktemp -q` if dialog --title "$1" \ --backtitle "Enter a version string for this backup or leave blank and press enter." \ --ok-label "Submit" --no-cancel \ --inputbox "Backup Version:" 0 0 2>$verstmp ; then bu_version=`cat $verstmp|sed 's/ /_/g'` if ! validate_text name $bu_version; then pause "Version string contains invalid characters. Valid characters are upper and lowercase letters, numbers, underscores, dashes, and periods. Try again." 0 0 else unset verstext_invalid rm -f $verstmp fi fi rm -f $verstmp done } function drivebackup() { # Back up a partition table or a drive # Takes two arguments. First is the full path device name. Second # is the id (value in seldevs) local retval=0 local devname=$1 local id=$2 # Determine effective backup method if [[ $bu_method = "auto" ]]; then eff_bu_method="${devbm[$devname]}" else eff_bu_method="$bu_method" fi # Determine filename local basefn=`echo $devname|rev|cut -d/ -f1|rev` # Save S.M.A.R.T. info for device if smartctl -a $devname >/dev/null 2>&1 ; then smartctl -a $devname > $IMAGE_PATH/$imagename/$basefn.sm inform "S.M.A.R.T. info for $devname saved." "2" log "S.M.A.R.T. info for $devname saved." log "`cat $IMAGE_PATH/$imagename/$basefn.sm|egrep -A3 -e \"Device:|Device Model:\"`" else inform "S.M.A.R.T. info not available for $devname." "2" log "S.M.A.R.T. info not available for $devname." fi # Save drive size info drivesize=`blockdev --getsize64 $devname` if echo $drivesize > $IMAGE_PATH/$imagename/$basefn.sz ; then inform "drive size info for $devname saved." "2" log "drive size info for $devname saved." else inform "ERROR: drive size info for $devname could not be saved." log "ERROR: drive size info for $devname could not be saved." FAIL=1 local retval=1 fi # Save id of device if echo $id > $IMAGE_PATH/$imagename/$basefn.id ; then inform "disk ID of $devname saved." "2" log "disk ID of $devname saved." else inform "ERROR: disk ID of $devname could not be saved." log "ERROR: disk ID of $devname could not be saved." FAIL=1 local retval=1 fi if [[ $eff_bu_method = "dd" ]]; then # If this is a dd backup, dd the drive inform "$eff_bu_method backup of $devname started..." "2" if monitor "dd if=$devname conv=sync,noerror|pv -s $drivesize|$ddb_comp|split --bytes=2000m --suffix-length=3 --numeric-suffixes - $IMAGE_PATH/$imagename/$basefn.dd." "pausable" ; then inform "$eff_bu_method backup of $devname succeeded." "2" log "$eff_bu_method backup of $devname succeeded." else inform "ERROR: $eff_bu_method $operation of $devname failed." log "ERROR: $eff_bu_method $operation of $devname failed." FAIL=1 local retval=1 fi elif [[ $eff_bu_method = "partimage" || $eff_bu_method = "fsarchiver" ]]; then # Determine if we have an MBR or GPT partition scheme local ptable_type=`fdisk -l $devname|grep "Disklabel type:"|cut -d: -f2|tr -d ' '` if [[ $ptable_type = "dos" ]]; then # Save fdisk output for device if fdisk -l $devname > $IMAGE_PATH/$imagename/$basefn.fd ; then inform "fdisk info for $devname saved." "2" log "fdisk info for $devname saved." else inform "ERROR: fdisk info for $devname could not be saved." log "ERROR: fdisk info for $devname could not be saved." FAIL=1 local retval=1 fi # Save MBR if dd if=$devname of=$IMAGE_PATH/$imagename/$basefn.mbr bs=512 count=1 ; then inform "MBR of $devname saved." "2" log "MBR of $devname saved." else inform "ERROR: MBR of $devname could not be saved." log "ERROR: MBR of $devname could not be saved." FAIL=1 local retval=1 fi # Save extended partition info if sfdisk -d $devname > $IMAGE_PATH/$imagename/$basefn.sf ; then inform "extended partition info of $devname saved." "2" log "extended partition info of $devname saved." else inform "ERROR: extended partition info of $devname could not be saved." log "ERROR: extended partition info of $devname could not be saved." FAIL=1 local retval=1 fi elif [[ $ptable_type = "gpt" ]]; then # Save gpt data for device if sgdisk -b $IMAGE_PATH/$imagename/$basefn.gpt $devname ; then inform "GPT partition info of $devname saved." "2" log "GPT partition info of $devname saved." else inform "ERROR: GPT partition info of $devname could not be saved." log "ERROR: GPT partition info of $devname could not be saved." FAIL=1 local retval=1 fi else inform "ERROR: Could not determine partition table type of $devname." log "ERROR: Could not determine partition table type of $devname." FAIL=1 local retval=1 fi fi sync return $retval } function driverestore() { # Restores MBRs and extended partition info or dd images to a drive # Takes two arguments. First is the full path image name. # Second is the backup method used local retval=0 local devname=$1 local eff_bu_method=$2 local basefn=`echo $devname|rev|cut -d/ -f1|rev` # Display S.M.A.R.T. info of original device log "Original drive was: `cat $IMAGE_PATH/$imagename/$basefn.sm|head -8|tail -4`" inform "Original drive was: `cat $IMAGE_PATH/$imagename/$basefn.sm|head -8|tail -4`" "4" # Display S.M.A.R.T. info of current device if smartctl -a $devname >/dev/null 2>&1 ; then log "Current drive is: `smartctl -a $devname|head -8|tail -4`" inform "Current drive is: `smartctl -a $devname|head -8|tail -4`" "4" else log "S.M.A.R.T. info not available for $devname." inform "S.M.A.R.T. info not available for $devname." "4" fi if [ "$eff_bu_method" = "dd" ]; then # If this was a dd backup, determine the compression level used if file $IMAGE_PATH/$imagename/$basefn.dd.000|grep -q bzip2; then bu_comp="high" elif file $IMAGE_PATH/$imagename/$basefn.dd.000|grep -q gzip; then bu_comp="normal" else bu_comp="low" fi setcomplevel # dd the image inform "$eff_bu_method restore of $devname started..." "2" if imgsize=`cat $IMAGE_PATH/$imagename/$basefn.sz` ; then progress_cmd="pv -s $imgsize" else progress_cmd="pv -prb" fi if monitor "cat $IMAGE_PATH/$imagename/$basefn.dd.*|$ddr_comp|$progress_cmd|dd of=$devname" "nopause" ; then inform "$eff_bu_method restore of $devname succeeded." "2" log "$eff_bu_method restore of $devname succeeded." else inform "ERROR: $eff_bu_method $operation of $devname failed." log "ERROR: $eff_bu_method $operation of $devname failed." FAIL=1 local retval=1 fi inform "Re-reading partition table of $devname." "2" log "Re-reading partition table of $devname." partprobe $devname elif [[ $eff_bu_method = "partimage" || $eff_bu_method = "fsarchiver" ]]; then # Determine the partition table type of the backup if [ -e $IMAGE_PATH/$imagename/$basefn.mbr ]; then # Restore MBR and extended partition info dd if=$IMAGE_PATH/$imagename/$basefn.mbr of=$devname >/dev/null 2>&1 if sfdisk $SFD_RESTORE_ARGS $devname < $IMAGE_PATH/$imagename/$basefn.sf >/dev/null 2>&1; then inform "$operation of MBR and extended partition info of $devname successful." "2" log "$operation of MBR and extended partition info of $devname successful." else partprobe $devname if ! sfdisk $SFD_RESTORE_ARGS $devname < $IMAGE_PATH/$imagename/$basefn.sf >/dev/null 2>&1; then inform "ERROR: $operation of MBR and extended partition info of $devname failed." log "ERROR: $operation of MBR and extended partition info of $devname failed." FAIL=1 local retval=1 else inform "$operation of MBR and extended partition info of $devname successful." "2" log "$operation of MBR and extended partition info of $devname successful." fi fi inform "Re-reading partition table of $devname." "2" log "Re-reading partition table of $devname." partprobe $devname elif [ -e $IMAGE_PATH/$imagename/$basefn.gpt ]; then if sgdisk -Z $devname >/dev/null 2>&1 ; then inform "Partition table of $devname cleared." "2" log "Partition table of $devname cleared." else inform "ERROR: Partition table of $devname could not be cleared." log "ERROR: Partition table of $devname could not be cleared." FAIL=1 local retval=1 fi if sgdisk -gl $IMAGE_PATH/$imagename/$basefn.gpt $devname ; then inform "$operation of GPT partition table of $devname successful." "2" log "$operation of GPT partition table of $devname successful." else partprobe $devname if ! sgdisk -gl $IMAGE_PATH/$imagename/$basefn.gpt $devname ; then inform "ERROR: $operation of GPT partition table of $devname failed." log "ERROR: $operation of GPT partition table of $devname failed." FAIL=1 local retval=1 else inform "$operation of GPT partition table of $devname successful." "2" log "$operation of GPT partition table of $devname successful." fi fi inform "Re-reading partition table of $devname." "2" log "Re-reading partition table of $devname." partprobe $devname else inform "ERROR: Could not determine partition table type of the backup." log "ERROR: Could not determine partition table type of the backup." FAIL=1 local retval=1 fi fi sync return $retval } function partbackup() { # Back up a partition # Takes two arguments. First is the full path device name. Second # is the id (value in seldevs) local retval=0 local devname=$1 local id=$2 # Determine effective backup method if [[ $bu_method = "auto" ]]; then eff_bu_method="${devbm[$devname]}" else eff_bu_method="$bu_method" fi # Determine filename local basefn=`echo $devname|rev|cut -d/ -f1|rev` # Save partition size info partsize=`blockdev --getsize64 $devname` if echo $partsize > $IMAGE_PATH/$imagename/$basefn.sz ; then inform "partition size info for $devname saved." "2" log "partition size info for $devname saved." else inform "ERROR: partition size info for $devname could not be saved." "2" log "ERROR: partition size info for $devname could not be saved." FAIL=1 local retval=1 fi # Save id of device if echo $id > $IMAGE_PATH/$imagename/$basefn.id ; then inform "filesystem/partition type of $devname saved." "2" log "filesystem/partition type of $devname saved." else inform "ERROR: filesystem/partition type of $devname could not be saved." log "ERROR: filesystem/partition type of $devname could not be saved." FAIL=1 local retval=1 fi if [[ $eff_bu_method = "dd" ]]; then # If this is a dd backup, dd the drive inform "$eff_bu_method backup of $devname started..." "2" if monitor "dd if=$devname conv=sync,noerror|pv -s $partsize|$ddb_comp|split --bytes=2000m --suffix-length=3 --numeric-suffixes - $IMAGE_PATH/$imagename/$basefn.dd." "pausable" ; then inform "$eff_bu_method backup of $devname succeeded." "2" log "$eff_bu_method backup of $devname succeeded." else inform "ERROR: $eff_bu_method backup of $devname failed." log "ERROR: $eff_bu_method backup of $devname failed." FAIL=1 local retval=1 fi elif [[ $eff_bu_method = "partimage" || $eff_bu_method = "fsarchiver" ]]; then if echo "$devname"|grep -q -o -e "c[0-9]+d[0-9]+p[0-9]+|nvme[0-9]+n[0-9]+p[0-9]+" ; then local drive=`echo $devname|rev|cut -c3-|rev` else local drive=`echo $devname|rev|cut -c2-|rev` fi if fdisk -l $drive|grep $devname|grep -q "Linux swap"; then # partition is a swap partition if `file -s $devname |grep -q swap` ; then inform "Recognized $devname as Linux swap partition." log "Recognized $devname as Linux swap partition." swptxt=`blkid $devname` else inform "Warning: $devname has a partition type of Linux swap, but is not" inform "formatted as swap." log "Warning: $devname has a partition type of Linux swap, but is not" log "formatted as swap." swptxt="swap" fi if [[ $eff_bu_method = "partimage" ]]; then suffix="img.000";fi if [[ $eff_bu_method = "fsarchiver" ]]; then suffix="fsa";fi if echo $swptxt >$IMAGE_PATH/$imagename/$basefn.$suffix ; then if echo $swptxt|grep -q UUID ; then inform "swap partition UUID for $devname saved." "2" log "swap partition UUID for $devname saved." else inform "unformatted swap partition $devname saved." "2" log "unformatted swap partition $devname saved." fi else inform "ERROR: swap partition $devname could not be saved." log "ERROR: swap partition $devname could not be saved." FAIL=1 local retval=1 fi else if [[ $eff_bu_method = "partimage" ]]; then inform "$eff_bu_method backup of $devname started..." "2" if monitor "partimage $pi_comp $PI_BACKUP_ARGS $devname $IMAGE_PATH/$imagename/$basefn.img" "pausable" ; then inform "$eff_bu_method backup of $devname succeeded." "2" log "$eff_bu_method backup of $devname succeeded." else inform "ERROR: partimage backup of $devname failed." log "ERROR: partimage backup of $devname failed." FAIL=1 local retval=1 fi fi if [[ $eff_bu_method = "fsarchiver" ]]; then inform "$eff_bu_method backup of $devname started..." "2" if monitor "fsarchiver $FA_BACKUP_ARGS $fa_comp $IMAGE_PATH/$imagename/$basefn.fsa $devname" "pausable" ; then inform "$eff_bu_method backup of $devname succeeded." "2" log "$eff_bu_method backup of $devname succeeded." else inform "ERROR: fsarchiver backup of $devname failed." log "ERROR: fsarchiver backup of $devname failed." FAIL=1 local retval=1 fi fi fi fi sync return $retval } function partrestore() { # Restore a partition # Takes two arguments. First is the full path image name. # Second is the backup method used local retval=0 local devname=$1 local eff_bu_method=$2 local basefn=`echo $devname|rev|cut -d/ -f1|rev` if [[ $eff_bu_method = "dd" ]]; then # If this was a dd backup, determine the compression level used if file $IMAGE_PATH/$imagename/$basefn.dd.000|grep -q bzip2; then bu_comp="high" elif file $IMAGE_PATH/$imagename/$basefn.dd.000|grep -q gzip; then bu_comp="normal" else bu_comp="low" fi setcomplevel # dd the image log "$eff_bu_method restore of $devname started..." inform "$eff_bu_method restore of $devname started..." "2" if imgsize=`cat $IMAGE_PATH/$imagename/$basefn.sz` ; then progress_cmd="pv -s $imgsize" else progress_cmd="pv -prb" fi if monitor "cat $IMAGE_PATH/$imagename/$basefn.dd.*|$ddr_comp|$progress_cmd|dd of=$devname" "nopause" ; then inform "$eff_bu_method restore of $devname succeeded." "2" log "$eff_bu_method restore of $devname succeeded." else inform "ERROR: $eff_bu_method $operation of $devname failed." log "ERROR: $eff_bu_method $operation of $devname failed." FAIL=1 local retval=1 fi elif [[ $eff_bu_method = "partimage" || $eff_bu_method = "fsarchiver" ]]; then log "$eff_bu_method restore of $devname started..." inform "$eff_bu_method restore of $devname started..." "2" if [[ $eff_bu_method = "partimage" ]]; then suffix="img.000" fi if [[ $eff_bu_method = "fsarchiver" ]]; then suffix="fsa" fi # Detect swap partition and create appropriately if file $IMAGE_PATH/$imagename/$basefn.$suffix|grep -q "ASCII text"&&grep -q swap $IMAGE_PATH/$imagename/$basefn.$suffix; then if grep -q UUID $IMAGE_PATH/$imagename/$basefn.$suffix; then log "$devname is a Linux swap partition with saved UUID. Running mkswap and setting UUID." inform "$devname is a Linux swap partition with saved UUID. Running mkswap and setting UUID." "2" if mkswap -U "$(cat $IMAGE_PATH/$imagename/$basefn.$suffix|grep -o -E ' UUID="[^ ]+"'|cut -d'"' -f2)" $devname ; then log "Created Linux swap partition with saved UUID on $devname" inform "Created Linux swap partition with saved UUID on $devname" "2" else log "ERROR: Could not create Linux swap partition with saved UUID on $devname" inform "ERROR: Could not create Linux swap partition with saved UUID on $devname" FAIL=1 local retval=1 fi else log "$devname is a Linux swap partition without saved UUID. Running mkswap." inform "$devname is a Linux swap partition without saved UUID. Running mkswap." "2" if mkswap $devname ; then log "Created Linux swap partition with new UUID on $devname" inform "Created Linux swap partition with new UUID on $devname" "2" else log "ERROR: Could not create Linux swap partition on $devname" inform "ERROR: Could not create Linux swap partition on $devname" FAIL=1 local retval=1 fi fi else # For non-swap partitions, run the appropriate restore command case "$eff_bu_method" in "partimage" ) if monitor "partimage $PI_RESTORE_ARGS $devname $IMAGE_PATH/$imagename/$basefn.img.000" "nopause" ; then inform "$eff_bu_method restore of $devname succeeded." "2" log "$eff_bu_method restore of $devname succeeded." else inform "ERROR: $eff_bu_method $operation of $devname failed." log "ERROR: $eff_bu_method $operation of $devname failed." FAIL=1 local retval=1 fi ;; "fsarchiver" ) if monitor "fsarchiver $FA_RESTORE_ARGS $IMAGE_PATH/$imagename/$basefn.fsa id=0,dest=$devname" "nopause" ; then inform "$eff_bu_method restore of $devname succeeded." "2" log "$eff_bu_method restore of $devname succeeded." else inform "ERROR: $eff_bu_method $operation of $devname failed." log "ERROR: $eff_bu_method $operation of $devname failed." FAIL=1 local retval=1 fi ;; esac fi fi sync return $retval } function method_analyze() { # Analyzes devices in seldevs array to determine best backup method # Populates the devbm associative array. Keys are device pathnames. # Values are the backup methods. # Examine selected devices for adev in ${!seldevs[@]} ; do if echo $adev|egrep -q -o -e "/dev/sd[a-z]$|/dev/cciss/c[0-9]d[0-9]$|/dev/i2o/hd[a-z]$|/dev/nvme[0-9]n[0-9]$" ; then # device is a drive if fdisk -l $adev |grep -q "Disklabel type:" ; then # not really partimage, but will get the MBR or the GPT info, which will work. devbm[$adev]="partimage" else # no partition table that we can see. Have to dd it. devbm[$adev]="dd" fi fi if echo $adev|egrep -q -o -e "sd[a-z][1-9]$|c[0-9]d[0-9]p[0-9]$|hd[a-z][1-9]$|nvme[0-9]n[0-9]p[0-9]" ; then # device is a partition case ${seldevs[$adev]} in "ext4 filesystem" | \ "reiser4 filesystem" | \ "btrfs filesystem" \ ) devbm[$adev]="fsarchiver" ;; "vfat filesystem" | \ "hpfs filesystem" | \ "ufs filesystem" | \ "hfs filesystem" \ ) devbm[$adev]="partimage" ;; "ext2 filesystem" | \ "ext3 filesystem" | \ "reiserfs filesystem" | \ "jfs filesystem" | \ "xfs filesystem" | \ "swap partition" | \ "ntfs filesystem" \ ) devbm[$adev]="partimage" ;; * ) devbm[$adev]="dd" ;; esac fi done return 0 } # -------------- DISK UTILITY FUNCTIONS --------------- function diskutil() { dutemp=`mktemp -q` while true; do flush_input dialog --no-cancel \ --title "DISK UTILITIES" \ --menu "Please enter choice:" 13 50 6 \ "1" "Clone disk" \ "2" "Blank disk" \ "3" "Test disk for bad blocks" \ "4" "Set up backup media" \ "?" "Disk utility help" \ "M" "Return to main menu" 2> $dutemp utilchoice=`cat $dutemp` rm -f $dutemp case $utilchoice in "M" ) return 0 ;; "?" ) if [ -r "$HELP_PATH/diskutil.txt" ]; then dialog --title "RescueBRU Help - Disk utilities" --no-shadow --textbox $HELP_PATH/diskutil.txt 25 80 else pause "Help file $HELP_PATH/diskutil.txt could not be found. Check your installation." fi ;; 4 ) media_reqd=1 if media "mount" ; then mediasetup media "unmount" else pause "Could not mount backup media. Unable to perform media setup." fi ;; 3 ) unset media_reqd do_badblocks ;; 2 ) unset media_reqd do_blank ;; 1 ) unset media_reqd do_clone ;; esac done } function do_clone() { operation="clone" operation_log "on" for key in ${!blockdevs[@]}; do unset blockdevs[$key]; done for key in ${!seldevs[@]}; do unset seldevs[$key]; done # Select source/destination drives if ! clone_select ; then pause "Unable to select source and destination drives for clone." log "Unable to select source and destination drives for clone." operation_log "off" return 1 fi # Warn if target is smaller than source if [ $(blockdev --getsize64 $targetdev) -lt $(blockdev --getsize64 $sourcedev) ]; then if ! approved "The target device is smaller than the source device. All data will not fit. Continue?" "n" ; then return 1 fi fi if confirm_actions ; then # Clone the drive if ! clone.sh $sourcedev $targetdev ; then pause "Clone failed." log "Clone failed." operation_log "off" return 1 else inform "clone completed." "2" log "clone completed." # reread target device partition table if echo "$targetdev"|egrep -q -o -e "sd[a-z][1-9]$|c[0-9]d[0-9]p[0-9]$|hd[a-z][1-9]$|nvme[0-9]n[0-9]p[0-9]$" ; then if echo "$targetdev"|grep -q -o -e "c[0-9]+d[0-9]+p[0-9]+|nvme[0-9]+n[0-9]+p[0-9]+" ; then local drive=`echo $targetdev|rev|cut -c3-|rev` else local drive=`echo $targetdev|rev|cut -c2-|rev` fi else local drive="$targetdev" fi inform "Re-reading partition table of $drive..." log "Re-reading partition table of $drive" partprobe $drivebackup inform "Re-reading partition table of $drive...done." case $postaction in "Return to menu" ) operation_log "off" return 0 ;; "Reboot" ) log "Clone completed. Starting automatic reboot..." if ! shutdown_proc "reboot" ; then inform "Reboot aborted." "2" log "Reboot aborted." operation_log "off" return 0 fi ;; "Shut down" ) log "Clone completed. Starting automatic shutdown..." if ! shutdown_proc "shutdown" ; then inform "Shutdown aborted." "2" log "Shutdown aborted." operation_log "off" return 0 fi ;; esac fi else inform "Operation aborted." "2" log "Operation aborted." operation_log "off" flush_input pause "Press any key to return to the disk utilities menu." return 0 fi } function do_blank() { operation="blank" operation_log "on" for key in ${!blockdevs[@]}; do unset blockdevs[$key]; done for key in ${!seldevs[@]}; do unset seldevs[$key]; done # Select target drive if ! blank_select ; then inform "Unable to select target drive for blanking." "2" log "Unable to select target drive for blanking." flush_input pause "Press any key to return to the disk utilities menu." operation_log "off" return 1 fi if confirm_actions ; then # Blank the drive blank.sh $targetdev inform "blanking completed." log "blanking completed." # reread target device partition table if echo "$targetdev"|egrep -q -o -e "sd[a-z][1-9]$|c[0-9]d[0-9]p[0-9]$|hd[a-z][1-9]$|nvme[0-9]n[0-9]p[0-9]$" ; then if echo "$targetdev"|grep -q -o -e "c[0-9]+d[0-9]+p[0-9]+|nvme[0-9]+n[0-9]+p[0-9]+" ; then local drive=`echo $targetdev|rev|cut -c3-|rev` else local drive=`echo $targetdev|rev|cut -c2-|rev` fi else local drive="$targetdev" fi inform "Re-reading partition table of $drive..." log "Re-reading partition table of $drive" partprobe $drivebackup inform "Re-reading partition table of $drive...done." case $postaction in "Return to menu" ) operation_log "off" return 0 ;; "Reboot" ) log "Blanking completed. Starting automatic reboot..." if ! shutdown_proc "reboot" ; then inform "Reboot aborted." log "Reboot aborted." operation_log "off" return 0 fi ;; "Shut down" ) log "Blanking completed. Starting automatic shutdown..." if ! shutdown_proc "shutdown" ; then inform "Shutdown aborted." log "Shutdown aborted." operation_log "off" return 0 fi ;; esac else inform "Operation aborted." log "Operation aborted." operation_log "off" flush_input pause "Press any key to return to the disk utilities menu." return 0 fi } function do_badblocks() { operation="badblocks test" operation_log "on" for key in ${!blockdevs[@]}; do unset blockdevs[$key]; done for key in ${!seldevs[@]}; do unset seldevs[$key]; done # Select target drive if ! badblocks_select ; then inform "Unable to select target drive for badblocks testing." "2" log "Unable to select target drive for badblocks testing." flush_input pause "Press any key to return to the disk utilities menu." operation_log "off" return 1 fi # Select read-write option if ! rwoptselect ; then inform "Unable to select read-write option for badblocks testing." "2" log "Unable to select read-write option for badblocks testing." flush_input pause "Press any key to return to the disk utilities menu." operation_log "off" return 1 fi if confirm_actions ; then # test the drive if ! monitor "badblocks $badblocks_args $targetdev" "pausable" ; then pause "badblocks test failed." log "badblocks test failed." operation_log "off" return 1 else inform "badblocks test passed." log "badblocks test passed." case $postaction in "Return to menu" ) operation_log "off" return 0 ;; "Reboot" ) log "Badblocks test finished. Starting automatic reboot..." if ! shutdown_proc "reboot" ; then inform "Reboot aborted." log "Reboot aborted." operation_log "off" return 0 fi ;; "Shut down" ) log "Badblocks test finished. Starting automatic shutdown..." if ! shutdown_proc "shutdown" ; then inform "Shutdown aborted." log "Shutdown aborted." operation_log "off" return 0 fi ;; esac fi else inform "Operation aborted." log "Operation aborted." operation_log "off" flush_input pause "Press any key to return to the disk utilities menu." return 0 fi return 0 } function clone_select() { # Select source and destination devices for clone unset sourcedev unset targetdev for key in ${!blockdevs[@]}; do unset blockdevs[$key]; done if ! getblkdevs ; then pause "Couldn't get disk information." log "Couldn't get disk information." return 1 fi # Display a menu of devices for source for key in ${!seldevs[@]}; do unset seldevs[$key]; done if deviceselect "single" "Select SOURCE device for clone" ; then sourcedev="${!seldevs[@]}" else pause "Couldn't determine source device." log "Couldn't determine source device." return 1 fi # Exclude the source device from the target device list if ! exclude_dev "$sourcedev" ; then pause "Unable to exclude source device from target device list." return 1 fi # Display menu of devices for target for key in ${!seldevs[@]}; do unset seldevs[$key]; done if deviceselect "single" "Select TARGET device for clone" ; then targetdev="${!seldevs[@]}" else pause "Couldn't determine target device." log "Couldn't determine target device." return 1 fi # Warn if target drive is smaller than source device # if the target is a partition, get drive name if echo "$targetdev"|egrep -q -o -e "sd[a-z][1-9]$|c[0-9]d[0-9]p[0-9]$|hd[a-z][1-9]$|nvme[0-9]n[0-9]p[0-9]$" ; then if echo "$targetdev"|grep -q -o -e "c[0-9]+d[0-9]+p[0-9]+|nvme[0-9]+n[0-9]+p[0-9]+" ; then local targetdrive=`echo $targetdev|rev|cut -c3-|rev` else local targetdrive=`echo $targetdev|rev|cut -c2-|rev` fi else local targetdrive="$targetdev" fi sourcesize=`blockdev --getsize64 $sourcedev 2>/dev/null` targetsize=`blockdev --getsize64 $targetdrive 2>/dev/null` if [ $targetsize -lt $sourcesize ]; then if ! approved "The source device appears to be larger than the target drive. The data will probably not fit and the clone process will report failure. Proceed anyway?" "n"; then inform "Clone device selection aborted." log "Clone device selection aborted." unset sourcedev unset targetdev rm -f $LOG_PATH/clone*.log return 1 fi fi return 0 } function blank_select() { # Select target device for blanking unset targetdev for key in ${!blockdevs[@]}; do unset blockdevs[$key]; done if ! getblkdevs ; then pause "Couldn't get disk information." log "Couldn't get disk information." return 1 fi # Display menu of devices for target for key in ${!seldevs[@]}; do unset seldevs[$key]; done if deviceselect "single" "Select device to blank" ; then targetdev="${!seldevs[@]}" else pause "Couldn't determine target device." log "Couldn't determine target device." return 1 fi return 0 } function badblocks_select() { # Select target drive for testing unset targetdev for key in ${!blockdevs[@]}; do unset blockdevs[$key]; done if ! getblkdevs ; then pause "Couldn't get disk information." log "Couldn't get disk information." return 1 fi # Display menu of devices for target for key in ${!seldevs[@]}; do unset seldevs[$key]; done if deviceselect "single" "Select device to test" ; then targetdev="${!seldevs[@]}" else pause "Couldn't determine target device." log "Couldn't determine target device." return 1 fi return 0 } function rwoptselect() { # For badblocks test, select a read-only or write test rwtmp=`mktemp -q` if dialog --no-items --radiolist "Select type of badblocks test" 12 40 12 \ "read-only" "on" "write" "off" 2>$rwtmp ; then rwopt=`cat $rwtmp` case $rwopt in "read-only" ) badblocks_args="-vs" ;; "write" ) badblocks_args="-vws" ;; esac else pause "badblocks test type selection cancelled." log "badblocks test type selection cancelled." rm -f $rwtmp return 1 fi rm -f $rwtmp return 0 } # ------------ DEVICE FUNCTIONS ------------------ function getblkdevs() { # Populate associative array, blockdevs, of block devices. # Array keys will be absolute paths of devices. # Array values will be smartctl reported Device model or "disk" # for drive, "extpart" for an extended partition, # " filesystem" for a primary or logical partition # containing a filesystem, "swap partition" # for swap, or "unknown partition" if it couldn't be determined. for blkdev in `ls /dev/sd* /dev/cciss/c*d* /dev/i2o/hd* /dev/nvme*n* 2>/dev/null` ; do if echo $blkdev|egrep -q -o -e "/dev/sd[a-z]$|/dev/cciss/c[0-9]d[0-9]$|/dev/i2o/hd[a-z]$|/dev/nvme[0-9]n[0-9]$" ; then # it's a drive id=`smartctl -i $blkdev|egrep -e "Device:|Device Model:"|head -1|cut -d: -f2|xargs` if [[ $id = "" ]]; then id="disk"; fi blockdevs[$blkdev]="$id" else # it's a partition. determine its filesystem pdrive=`echo $blkdev|egrep -o -e "/dev/sd[a-z]|/dev/cciss/c[0-9]d[0-9]|/dev/i2o/hd[a-z]|/dev/nvme[0-9]n[0-9]"` if fdisk -l $pdrive|grep $blkdev|egrep -q -e "Extended|W95 Ext|Linux extended" ; then blockdevs[$blkdev]="extpart" else local fstype=`blkid $blkdev |egrep -o -e ' TYPE="[a-zA-Z0-9]*"'|cut -d"\"" -f2` if [[ $fstype = "swap" ]]; then blockdevs[$blkdev]="swap partition" elif [[ $fstype = "" ]]; then blockdevs[$blkdev]="unknown partition" else blockdevs[$blkdev]="$fstype filesystem" fi fi fi done if [ $(echo ${#blockdevs[@]}) -eq 0 ]; then pause "No block devices found!" return 1 else return 0 fi } function deviceselect() { # Prompt for block devices on which to perform an operation # Parses the blockdevs array, removes excluded devices, # presents a checklist of drives on which to operate. # Takes two arguments. First, the string "single" or "multiple" that # controls whether a radiolist or checklist is displayed. Second, # the prompt string to be displayed. # Populates an associative array, seldevs. The keys are the absolute # paths of block devices, and the values are the partition types taken # from the blockdevs array for key in ${!seldevs[@]}; do unset seldevs[$key]; done for key in ${!devbm[@]}; do unset devbm[$key]; done if [ $# -ne 2 ]; then pause "$FUNCNAME : $LINENO :Not enough arguments." return 1 fi if [ "$1" = "single" ]; then listtype="--radiolist" elif [ "$1" = "multiple" ]; then listtype="--checklist" else pause "$FUNCNAME : $LINENO :Must specify \"single\" or \"multiple\"." return 1 fi prompttext=$2 # Generate a list of devices for the list dialog local listitems=( ) tagtmp=`mktemp -q` for dev in `echo ${!blockdevs[@]}|tr " " "\n"|sort|tr "\n" " "` ; do if [[ ${blockdevs[$dev]} = "extpart" ]]; then exclude_dev $dev;log "excluded $dev (extended partition)" fi if not_excluded $dev ; then listitems+=( "$dev" "${blockdevs[$dev]}" "off" ) ; fi done # Select devices flush_input while [ ! $finished ] ; do if ! dialog $listtype "$prompttext" 20 60 20 "${listitems[@]}" 2>$tagtmp ; then dialog --sleep 3 --aspect 80 --infobox "Device selection canceled." 0 0 rm -f $tagtmp return 1 fi for devname in `cat $tagtmp` ; do seldevs[$devname]="${blockdevs[$devname]}" done local finished=1 if [ ${#seldevs[@]} -eq 0 ]; then pause "You didn't select any devices. If this was your intent, press enter to return to the selection list and select Cancel." unset finished fi done rm -f $tagtmp return 0 } function getimages() { # Populate an associative array, images, with backup images. # Array keys will be absolute path block device names. # Values will be backup method. # Populate an associative array, imgid, with with # disk id/filesystem type. # Array keys will be absolute path block device names. # Values will be disk id/filesystem type. # standard /dev/sdx images for img in `ls $IMAGE_PATH/$imagename/sd* 2>/dev/null` ; do if `echo $img|egrep -q -o -e "sd[a-z].mbr|sd[a-z].gpt"` ; then # MBR or GPT treated as partimage baseimgname=`echo $img|egrep -o -e 'sd[a-z].mbr|sd[a-z].gpt'|cut -d "." -f1` imgkey="/dev/$baseimgname" images[$imgkey]="partimage" if [ -e $IMAGE_PATH/$imagename/$baseimgname.id ]; then imgid[$imgkey]=`cat $IMAGE_PATH/$imagename/$baseimgname.id` else imgid[$imgkey]="MBR" fi elif `echo $img|egrep -q -o -e "sd[a-z][0-9]+.img.000"` ; then # partimage partition baseimgname=`echo $img|egrep -o -e 'sd[a-z][0-9]+.img.000'|cut -d "." -f1` imgkey="/dev/$baseimgname" images[$imgkey]="partimage" if [ -e "$IMAGE_PATH/$imagename/$baseimgname.id" ]; then imgid[$imgkey]=`cat $IMAGE_PATH/$imagename/$baseimgname.id` else imgid[$imgkey]="partition" fi elif `echo $img|egrep -q -o -e "sd[a-z][0-9]+.fsa"` ; then # fsarchiver partition baseimgname=`echo $img|egrep -o -e 'sd[a-z][0-9]+.fsa'|cut -d "." -f1` imgkey="/dev/$baseimgname" images[$imgkey]="fsarchiver" if [ -e "$IMAGE_PATH/$imagename/$baseimgname.id" ]; then imgid[$imgkey]=`cat $IMAGE_PATH/$imagename/$baseimgname.id` else imgid[$imgkey]="partition" fi elif `echo $img|egrep -q -o -e "sd[a-z][0-9]+.dd.000"` ; then # dd partition baseimgname=`echo $img|egrep -o -e 'sd[a-z][0-9]+.dd.000'|cut -d "." -f1` imgkey="/dev/$baseimgname" images[$imgkey]="dd" if [ -e "$IMAGE_PATH/$imagename/$baseimgname.id" ]; then imgid[$imgkey]=`cat $IMAGE_PATH/$imagename/$baseimgname.id` else imgid[$imgkey]="partition image" fi elif `echo $img|egrep -q -o -e "sd[a-z].dd.000"` ; then # dd disk baseimgname=`echo $img|egrep -o -e 'sd[a-z].dd.000'|cut -d "." -f1` imgkey="/dev/$baseimgname" images[$imgkey]="dd" if [ -e "$IMAGE_PATH/$imagename/$baseimgname.id" ]; then imgid[$imgkey]=`cat $IMAGE_PATH/$imagename/$baseimgname.id` else imgid[$imgkey]="disk image" fi fi done # HP CISS images for img in `ls $IMAGE_PATH/$imagename/c*d* 2>/dev/null` ; do if `echo $img|egrep -q -o -e "c[0-9]+d[0-9]+.mbr"` ; then # MBR treated as partimage baseimgname=`echo $img|egrep -o -e 'c[0-9]+d[0-9]+.mbr'|cut -d "." -f1` imgkey="/dev/cciss/$baseimgname" images[$imgkey]="partimage" if [ -e "$IMAGE_PATH/$imagename/$baseimgname.id" ]; then imgid[$imgkey]=`cat $IMAGE_PATH/$imagename/$baseimgname.id` else imgid[$imgkey]="MBR" fi elif `echo $img|egrep -q -o -e "c[0-9]+d[0-9]+p[0-9]+.img.000"` ; then # partimage partition baseimgname=`echo $img|egrep -o -e 'c[0-9]+d[0-9]+p[0-9]+.img.000'|cut -d "." -f1` imgkey="/dev/cciss/$baseimgname" images[$imgkey]="partimage" if [ -e "$IMAGE_PATH/$imagename/$baseimgname.id" ]; then imgid[$imgkey]=`cat $IMAGE_PATH/$imagename/$baseimgname.id` else imgid[$imgkey]="partition" fi elif `echo $img|egrep -q -o -e "c[0-9]+d[0-9]+p[0-9]+.fsa"` ; then # fsarchiver partition baseimgname=`echo $img|egrep -o -e 'c[0-9]+d[0-9]+p[0-9]+.fsa'|cut -d "." -f1` imgkey="/dev/cciss/$baseimgname" images[$imgkey]="fsarchiver" if [ -e "$IMAGE_PATH/$imagename/$baseimgname.id" ]; then imgid[$imgkey]=`cat $IMAGE_PATH/$imagename/$baseimgname.id` else imgid[$imgkey]="partition" fi elif `echo $img|egrep -q -o -e "c[0-9]+d[0-9]+p[0-9]+.dd.000"` ; then # dd partition baseimgname=`echo $img|egrep -o -e 'c[0-9]+d[0-9]+p[0-9]+.dd.000'|cut -d "." -f1` imgkey="/dev/cciss/$baseimgname" images[$imgkey]="dd" if [ -e "$IMAGE_PATH/$imagename/$baseimgname.id" ]; then imgid[$imgkey]=`cat $IMAGE_PATH/$imagename/$baseimgname.id` else imgid[$imgkey]="partition image" fi elif `echo $img|egrep -q -o -e "c[0-9]+d[0-9]+.dd.000"` ; then # dd disk baseimgname=`echo $img|egrep -o -e 'c[0-9]+d[0-9]+.dd.000'|cut -d "." -f1` imgkey="/dev/cciss/$baseimgname" images[$imgkey]="dd" if [ -e "$IMAGE_PATH/$imagename/$baseimgname.id" ]; then imgid[$imgkey]=`cat $IMAGE_PATH/$imagename/$baseimgname.id` else imgid[$imgkey]="disk image" fi fi done # Adaptec i2o images for img in `ls $IMAGE_PATH/$imagename/hd* 2>/dev/null` ; do if `echo $img|egrep -q -o -e "hd[a-z].mbr"` ; then # MBR treated as partimage baseimgname=`echo $img|egrep -o -e 'hd[a-z].mbr'|cut -d "." -f1` imgkey="/dev/i2o/$baseimgname" images[$imgkey]="partimage" if [ -e "$IMAGE_PATH/$imagename/$baseimgname.id" ]; then imgid[$imgkey]=`cat $IMAGE_PATH/$imagename/$baseimgname.id` else imgid[$imgkey]="MBR" fi elif `echo $img|egrep -q -o -e "hd[a-z][0-9]+.img.000"` ; then # partimage partition baseimgname=`echo $img|egrep -o -e 'hd[a-z][0-9]+.img.000'|cut -d "." -f1` imgkey="/dev/i2o/$baseimgname" images[$imgkey]="partimage" if [ -e "$IMAGE_PATH/$imagename/$baseimgname.id" ]; then imgid[$imgkey]=`cat $IMAGE_PATH/$imagename/$baseimgname.id` else imgid[$imgkey]="partition" fi elif `echo $img|egrep -q -o -e "hd[a-z][0-9]+.fsa"` ; then # fsarchiver partition baseimgname=`echo $img|egrep -o -e 'hd[a-z][0-9]+.fsa'|cut -d "." -f1` imgkey="/dev/i2o/$baseimgname" images[$imgkey]="fsarchiver" if [ -e "$IMAGE_PATH/$imagename/$baseimgname.id" ]; then imgid[$imgkey]=`cat $IMAGE_PATH/$imagename/$baseimgname.id` else imgid[$imgkey]="partition" fi elif `echo $img|egrep -q -o -e "hd[a-z][0-9]+.dd.000"` ; then # dd partition baseimgname=`echo $img|egrep -o -e 'hd[a-z][0-9]+.dd.000'|cut -d "." -f1` imgkey="/dev/i2o/$baseimgname" images[$imgkey]="dd" if [ -e "$IMAGE_PATH/$imagename/$baseimgname.id" ]; then imgid[$imgkey]=`cat $IMAGE_PATH/$imagename/$baseimgname.id` else imgid[$imgkey]="partition image" fi elif `echo $img|egrep -q -o -e "hd[a-z].dd.000"` ; then # dd disk baseimgname=`echo $img|egrep -o -e 'hd[a-z].dd.000'|cut -d "." -f1` imgkey="/dev/i2o/$baseimgname" images[$imgkey]="dd" if [ -e "$IMAGE_PATH/$imagename/$baseimgname.id" ]; then imgid[$imgkey]=`cat $IMAGE_PATH/$imagename/$baseimgname.id` else imgid[$imgkey]="disk image" fi fi done # NVMe images for img in `ls $IMAGE_PATH/$imagename/nvme*n* 2>/dev/null` ; do if `echo $img|egrep -q -o -e "nvme[0-9]+n[0-9]+.mbr|nvme[0-9]+n[0-9]+.gpt"` ; then # MBR or GPT treated as partimage baseimgname=`echo $img|egrep -o -e 'nvme[0-9]+n[0-9]+.mbr|nvme[0-9]+n[0-9]+.gpt'|cut -d "." -f1` imgkey="/dev/$baseimgname" images[$imgkey]="partimage" if [ -e "$IMAGE_PATH/$imagename/$baseimgname.id" ]; then imgid[$imgkey]=`cat $IMAGE_PATH/$imagename/$baseimgname.id` else imgid[$imgkey]="MBR" fi elif `echo $img|egrep -q -o -e "nvme[0-9]+n[0-9]+p[0-9]+.img.000"` ; then # partimage partition baseimgname=`echo $img|egrep -o -e 'nvme[0-9]+n[0-9]+p[0-9]+.img.000'|cut -d "." -f1` imgkey="/dev/$baseimgname" images[$imgkey]="partimage" if [ -e "$IMAGE_PATH/$imagename/$baseimgname.id" ]; then imgid[$imgkey]=`cat $IMAGE_PATH/$imagename/$baseimgname.id` else imgid[$imgkey]="partition" fi elif `echo $img|egrep -q -o -e "nvme[0-9]+n[0-9]+p[0-9]+.fsa"` ; then # fsarchiver partition baseimgname=`echo $img|egrep -o -e 'nvme[0-9]+n[0-9]+p[0-9]+.fsa'|cut -d "." -f1` imgkey="/dev/$baseimgname" images[$imgkey]="fsarchiver" if [ -e "$IMAGE_PATH/$imagename/$baseimgname.id" ]; then imgid[$imgkey]=`cat $IMAGE_PATH/$imagename/$baseimgname.id` else imgid[$imgkey]="partition" fi elif `echo $img|egrep -q -o -e "nvme[0-9]+n[0-9]+p[0-9]+.dd.000"` ; then # dd partition baseimgname=`echo $img|egrep -o -e 'nvme[0-9]+n[0-9]+p[0-9]+.dd.000'|cut -d "." -f1` imgkey="/dev/$baseimgname" images[$imgkey]="dd" if [ -e "$IMAGE_PATH/$imagename/$baseimgname.id" ]; then imgid[$imgkey]=`cat $IMAGE_PATH/$imagename/$baseimgname.id` else imgid[$imgkey]="partition image" fi elif `echo $img|egrep -q -o -e "nvme[0-9]+n[0-9]+.dd.000"` ; then # dd disk baseimgname=`echo $img|egrep -o -e 'nvme[0-9]+n[0-9]+.dd.000'|cut -d "." -f1` imgkey="/dev/$baseimgname" images[$imgkey]="dd" if [ -e "$IMAGE_PATH/$imagename/$baseimgname.id" ]; then imgid[$imgkey]=`cat $IMAGE_PATH/$imagename/$baseimgname.id` else imgid[$imgkey]="disk image" fi fi done if [[ ${#images[@]} -eq 0 ]]; then return 1 else return 0 fi } function imageselect() { # Select device images to be restored # Parses the images array, presents a checklist of # images to restore. Returns an associative array, selimages. # Array keys are absolute path device names to restore. # Values are the backup methods used for key in ${!selimages[@]}; do unset selimages[$key]; done for key in ${!imgid[@]}; do unset imgid[$key]; done # Generate a list of images for the checklist dialog local listitems=( ) tagtmp=`mktemp -q` for img in `echo ${!images[@]}|tr " " "\n"|sort|tr "\n" " "` ; do listitems+=( "$img" "${imgid[$img]}" "off" ) done # Select images flush_input while [ ! $finished ] ; do if ! dialog --checklist "Select images to restore" 20 60 20 "${listitems[@]}" 2>$tagtmp ; then dialog --sleep 3 --aspect 80 --infobox "Image selection canceled." 0 0 rm -f $tagtmp return 1 fi for imgname in `cat $tagtmp` ; do selimages[$imgname]="${images[$imgname]}" done local finished=1 if [ ${#selimages[@]} -eq 0 ]; then pause "You didn't select any images. If this was your intent, press enter to return to the selection list and select Cancel." unset finished fi done rm -f $tagtmp return 0 } function exclude_dev() { # add a device to the exclude array # takes 1 parameter, a device pathname exclude_list=( ${exclude_list[@]} $1 ) return 0 } function not_excluded() { # Tests to see if a device is in the device exclude list. # Takes one parameter, a device path # Returns 0 if device is not in list or 1 if it is. pathnm=$1 for lst_mem in ${exclude_list[@]}; do if [ "$pathnm" = "$lst_mem" ]; then return 1 fi done return 0 } # ------------- PROGRAM SETTING FUNCTIONS ------------ function defaultsettings() { # Restore program settings to defaults media_type=$MEDIA_TYPE_DEF bu_method=$BU_METHOD_DEF postaction=$POSTACTION_DEF netsetup_type=$NETSETUP_TYPE backup_server_type=$BACKUP_SERVER_TYPE bu_comp=$BU_COMP_DEF remote_dir=$NFS_REMOTE_DIR_DEF } function settings() { smtemp=`mktemp -q` while true; do flush_input dialog --no-cancel \ --title "RescueBRU Settings" \ --menu "" 14 79 9 \ "1" "Select backup media type (currently $media_type)" \ "2" "Select backup method (currently $bu_method)" \ "3" "Select backup compression level (currently $bu_comp)" \ "4" "Set action after successful operation (currently $postaction)" \ "5" "Select backup server type (currently $backup_server_type)" \ "6" "Select network setup type (currently $netsetup_type)" \ "7" "Set remote NFS share (currently $remote_dir)" \ "R" "Reset all to defaults" \ "M" "Return to main menu" 2> $smtemp setch=`cat $smtemp` rm -f $smtemp case $setch in "M" ) return 0 ;; "R" ) defaultsettings ;; 7 ) setremotedir ;; 6 ) netsetupselect ;; 5 ) servertypeselect ;; 4 ) setpostaction ;; 3 ) complvlselect ;; 2 ) methodselect ;; 1 ) mediaselect ;; esac done } function setremotedir() { orig_remote_dir=$remote_dir local rdir_invalid=1 while [ $rdir_invalid ]; do # Prompt for new remote directory name rdnametmp=`mktemp -q` if dialog --inputbox "Please enter remote NFS share name" \ 8 50 "$remote_dir" 2>$rdnametmp ; then remote_dir=`cat $rdnametmp` if ! validate_text path $remote_dir; then pause "Share name contains invalid characters. Valid characters are forward slash, upper and lowercase letters, numbers, underscore, and dash. Try again." 0 0 elif [ "$remote_dir" = "" ]; then pause "Share name cannot be blank. \nTry again." elif [ "$imagename" = "lost+found" ]; then pause "\"lost+found\" is not a valid share name. \nChoose another." else unset rdir_invalid rm -f $rdnametmp fi else inform "Share name entry aborted." rm -f $rdnametmp remote_dir=$orig_remote_dir return 1 fi done log "New NFS share name is $remote_dir" } function complvlselect() { # Set compression level for backups clstemp=`mktemp -q` while true ; do if ! dialog --default-item "$bu_comp" --menu "Select compression level for backups (currently $bu_comp)" 12 40 8 \ "L" "low" \ "N" "normal" \ "H" "high" \ "?" "Compression level help" 2> $clstemp ; then rm -f $clstemp return 0 else buc=`cat $clstemp` rm -f $clstemp fi case $buc in "L" ) bu_comp="low" log "Backup compression level set to $bu_comp." setcomplevel return 0 ;; "N" ) bu_comp="normal" log "Backup compression level set to $bu_comp." setcomplevel return 0 ;; "H" ) bu_comp="high" log "Backup compression level set to $bu_comp." setcomplevel return 0 ;; "?" ) if [ -r "$HELP_PATH/bucomp.txt" ]; then dialog --title "RescueBRU Help - Backup Compression" \ --no-shadow --textbox $HELP_PATH/bucomp.txt 25 80 else pause "Help file $HELP_PATH/bucomp.txt could not be found. Check your installation." fi ;; esac done } function setcomplevel() { # Sets the compression level arguments appropriately for the # different backup methods case $bu_comp in "low" ) ddb_comp="cat" ddr_comp="cat" pi_comp="-z0" fa_comp="-z1" return 0 ;; "normal" ) ddb_comp="gzip -cq" ddr_comp="gzip -dq" pi_comp="-z1" fa_comp="-z3" return 0 ;; "high" ) ddb_comp="bzip2 -cq" ddr_comp="bzip2 -dq" pi_comp="-z2" fa_comp="-z7" return 0 ;; * ) ddb_comp="gzip -cq" ddr_comp="gzip -dq" pi_comp="-z1" fa_comp="-z3" return 0 ;; esac } function netsetupselect() { # Select the type of network setup nsstemp=`mktemp -q` if ! dialog --menu "Select network setup type (currently $netsetup_type)" 10 50 8 \ "A" "auto" \ "M" "manual" 2> $nsstemp ; then rm -f $nsstemp return 0 else nst=`cat $nsstemp` rm -f $nsstemp fi case $nst in "M" ) netsetup_type="manual" ;; "A" ) netsetup_type="auto" ;; esac log "Net setup type set to $netsetup_type." } function servertypeselect() { # Select the type of backup server bsttemp=`mktemp -q` if ! dialog --menu "Select backup server type (currently $backup_server_type)" 10 50 8 \ "R" "RescueBRU Server" \ "A" "any DHCP server" 2> $bsttemp ; then rm -f $bsttemp return 0 else bst=`cat $bsttemp` rm -f $bsttemp fi case $bst in "R" ) backup_server_type="RescueBRU Server" ;; "A" ) backup_server_type="any DHCP server" ;; esac log "Backup server type set to $backup_server_type." } function setpostaction() { # Set the action taken after successful operation patemp=`mktemp -q` if ! dialog --menu "Select action after successful operation (currently $postaction)" 11 73 8 \ "M" "Return to menu" \ "R" "Reboot" \ "S" "Shutdown" 2> $patemp ; then rm -f $patemp return 0 else pa=`cat $patemp` rm -f $patemp fi case $pa in "M" ) postaction="Return to menu" ;; "R" ) postaction="Reboot" ;; "S" ) postaction="Shut Down" ;; esac log "Action after successful operation set to $postaction." } function methodselect() { # Prompt user to select backup method type bmtemp=`mktemp -q` while true ; do if ! dialog --default-item "$bu_method" --menu "Select backup method (currently $bu_method)" 13 50 8 \ "A" "auto" \ "P" "partimage" \ "F" "fsarchiver" \ "D" "dd" \ "?" "Backup method help" 2> $bmtemp ; then rm -f $bmtemp return 0 else bm=`cat $bmtemp` rm -f $bmtemp fi case $bm in "A" ) bu_method="auto" log "Backup method set to $bu_method." return 0 ;; "P" ) bu_method="partimage" log "Backup method set to $bu_method." return 0 ;; "F" ) bu_method="fsarchiver" log "Backup method set to $bu_method." return 0 ;; "D" ) bu_method="dd" log "Backup method set to $bu_method." return 0 ;; "?" ) if [ -r "$HELP_PATH/method.txt" ]; then dialog --title "RescueBRU Help - Backup method" \ --no-shadow --textbox $HELP_PATH/method.txt 25 80 else pause "Help file $HELP_PATH/method.txt could not be found. Check your installation." fi ;; esac done } function mediaselect() { # Prompt user to select media type. mttemp=`mktemp -q` while true ; do if ! dialog --default-item "$media_type" --menu "Select media type (currently $media_type)" 12 40 8 \ "N" "NFS" \ "U" "USB" \ "L" "Local" \ "?" "Media type help" 2> $mttemp ; then rm -f $mttemp return 0 else mt=`cat $mttemp` rm -f $mttemp fi case $mt in "N" ) media_type="NFS" log "Media type set to $media_type." return 0 ;; "U" ) media_type="USB" log "Media type set to $media_type." return 0 ;; "L" ) media_type="Local" log "Media type set to $media_type." return 0 ;; "?" ) if [ -r "$HELP_PATH/mediatype.txt" ]; then dialog --title "RescueBRU Help - Media type" \ --no-shadow --textbox $HELP_PATH/mediatype.txt 25 80 else pause "Help file $HELP_PATH/mediatype.txt could not be found. Check your installation." fi ;; esac done } # ------------------ MENUS -------------------------- function mainmenu() { mmtemp=`mktemp -q` if [ $LOCAL_SERVER_RUNNING ]; then flush_input dialog --clear --no-cancel \ --title "RescueBRU Main Menu"\ --menu "Welcome to Rescue CD Backup & Restore Utility __ Version $PROG_VERSION __ Please enter choice:" 22 50 22 \ "1" "Settings Menu" \ "2" "Do a backup" \ "3" "Do a restore" \ "4" "Disk utilities" \ "5" "Image management" \ "6" "View logs or notes" \ "7" "Show backups available on media" \ "?" "Help" \ "S" "Exit and return to rbruserver" 2> $mmtemp else flush_input dialog --clear --no-cancel \ --title "RescueBRU Main Menu"\ --menu "Welcome to Rescue CD Backup & Restore Utility __ Version $PROG_VERSION __ Please enter choice:" 22 50 22 \ "1" "Settings Menu" \ "2" "Do a backup" \ "3" "Do a restore" \ "4" "Disk utilities" \ "5" "Image management" \ "6" "View logs or notes" \ "7" "Show backups available on media" \ "?" "Help" \ "S" "Exit to shell" \ "R" "Exit and reboot" \ "P" "Exit and power down" 2> $mmtemp fi mmchoice=`cat $mmtemp` log "Option $mmchoice chosen from main menu." rm -f $mmtemp return 0 } function showhelp() { hmtemp=`mktemp -q` while true; do flush_input dialog --clear --no-cancel \ --title "HELP MENU"\ --menu "Please enter choice:" 15 40 8 \ "1" "General" \ "2" "Doing a backup" \ "3" "Restoring from a backup" \ "4" "Backup media" \ "5" "Backup method" \ "6" "Media type" \ "7" "Disk utilities" \ "M" "Exit help menu" 2> $hmtemp hmchoice=`cat $hmtemp` rm -f $hmtemp case $hmchoice in "M" ) return 0 ;; 7 ) if [ -r "$HELP_PATH/diskutil.txt" ]; then dialog --title "RescueBRU Help - Disk utilities" \ --no-shadow --textbox $HELP_PATH/diskutil.txt 25 80 else pause "Help file $HELP_PATH/diskutil.txt could not be found. Check your installation." fi ;; 6 ) echo if [ -r "$HELP_PATH/mediatype.txt" ]; then dialog --title "RescueBRU Help - Media type" \ --no-shadow --textbox $HELP_PATH/mediatype.txt 25 80 else pause "Help file $HELP_PATH/mediatype.txt could not be found. Check your installation." fi ;; 5 ) echo if [ -r "$HELP_PATH/method.txt" ]; then dialog --title "RescueBRU Help - Backup method" \ --no-shadow --textbox $HELP_PATH/method.txt 25 80 else pause "Help file $HELP_PATH/method.txt could not be found. Check your installation." fi ;; 4 ) echo if [ -r "$HELP_PATH/media.txt" ]; then dialog --title "RescueBRU Help - Backup media" \ --no-shadow --textbox $HELP_PATH/media.txt 25 80 else pause "Help file $HELP_PATH/media.txt could not be found. Check your installation." fi ;; 3 ) echo if [ -r "$HELP_PATH/restore.txt" ]; then dialog --title "RescueBRU Help - Restoring from a backup" \ --no-shadow --textbox $HELP_PATH/restore.txt 25 80 else pause "Help file $HELP_PATH/restore.txt could not be found. Check your installation." fi ;; 2 ) echo if [ -r "$HELP_PATH/backup.txt" ]; then dialog --title "RescueBRU Help - Doing a backup" \ --no-shadow --textbox $HELP_PATH/backup.txt 25 80 else pause "Help file $HELP_PATH/backup.txt could not be found. Check your installation." fi ;; 1 ) echo if [ -r "$HELP_PATH/general.txt" ]; then dialog --title "RescueBRU Help - General" \ --no-shadow --textbox $HELP_PATH/general.txt 25 80 else pause "Help file $HELP_PATH/general.txt could not be found. Check your installation." fi ;; esac done return 0 } # ------------------ PROCESS FUNCTIONS --------------- function cleanup() { # Perform cleanup functions prior to exit if [ "$CLIENT_NET_STATUS" -eq 1 ]; then mount $remote_addr:$remote_dir $MEDIA_MNT_DIR rm -f $MEDIA_MNT_DIR/$wkg_addr umount $MEDIA_MNT_DIR /etc/init.d/nfsmount stop >/dev/null 2>&1 ifconfig $interface 0.0.0.0 down fi return 0 } function confirm_actions() { # Display and confirm actions before an operation unset devarray declare -A devarray while true ; do local opsumtmp=`mktemp -q` local conflisttmp=`mktemp -q` local chgtmp=`mktemp -q` local devtmp=`mktemp -q` local notestmp=`mktemp -q` # Indexes for options: # 0 - Operation # 1 - Media type # 2 - Backup method # 3 - Compression level # 4 - Image name # 5 - Version # 6 - Devices # 7 - Source device # 8 - Target device # 9 - Type of test # 10 - Action on success # 11 - Notes # Define operation settings array opsettings=( "Operation" "Media type" "Backup method" "Compression level" "Image name" "Version" "Devices" "Source device" "Target device" "Type of test" "Action on success" "Notes") # Generate displayable list of devices from seldevs array for key in ${!devarray[@]}; do unset devarray[$key]; done echo "">$devtmp if [ "$operation" = "backup" ]; then for dev in ${!seldevs[@]} ; do devarray[$dev]="${seldevs[$dev]}" done elif [ "$operation" = "restore" ]; then for img in ${!selimages[@]} ; do devarray[$img]="${imgid[$img]}" done fi for dev in ${!devarray[@]} ; do if echo $dev|egrep -q -o -e "/dev/sd[a-z]$|/dev/cciss/c[0-9]d[0-9]$|/dev/i2o/hd[a-z]$|nvme[0-9]n[0-9]$"; then # it's a drive echo " $dev ${devarray[$dev]}">>$devtmp elif echo $dev|egrep -q -o -e "sd[a-z][1-9]$|c[0-9]d[0-9]p[0-9]$|hd[a-z][1-9]$|nvme[0-9]n[0-9]p[0-9]" ; then # it's a partition echo " $dev ${devarray[$dev]}">>$devtmp fi sort -k 1,1 $devtmp -o $devtmp dispdevs=`cat $devtmp` done # Generate displayable notes from $TEMP_NOTES if [ -s $TEMP_NOTES ]; then echo "">$notestmp cat $TEMP_NOTES>>$notestmp opnotes=`cat $notestmp 2>/dev/null` rm -f $notestmp else unset opnotes fi # Define variables that correspond to operation settings opvars=( "$operation" "$media_type" "$bu_method" "$bu_comp" "$imagename" "$bu_version" "$dispdevs" "$sourcedev" "$targetdev" "$rwopt" "$postaction" "$opnotes") # Define the appropriate settings for the operation case $operation in "backup" ) osindexes=( 0 1 2 3 4 5 6 10 11 );; "restore" ) osindexes=( 0 1 4 6 10 11 );; "blank" ) osindexes=( 0 8 10 );; "clone" ) osindexes=( 0 7 8 10 );; "badblocks test" ) osindexes=( 0 8 9 10 );; esac # Generate text for settings display and confirmation widgets for index in ${osindexes[@]} ; do log "${opsettings[$index]} : ${opvars[$index]}" echo "${opsettings[$index]} : ${opvars[$index]}">>$opsumtmp echo -n "\"$index\" \"${opsettings[$index]}\" \"off\" ">>$conflisttmp done # Only show the first 21 lines of the operations summary sed -i -e '22,$d' $opsumtmp # Display the widgets local dialog_script=`mktemp -q` echo "dialog --begin 0 0 --title \"Summary of Operations\" --no-collapse --no-ok --tailboxbg $opsumtmp 25 49 --and-widget --begin 0 50 --title \"Confirm Operations\" --no-collapse --defaultno --no-tags --checklist \"To proceed as shown, select OK.\nTo abort the operation, select CANCEL.\nTo make changes, check item(s) to change and select OK.\" 25 30 25 `cat $conflisttmp` 2>$chgtmp">$dialog_script if ! bash $dialog_script ; then # User selected CANCEL if approved "Are you sure you want to cancel the $operation?" "n"; then rm -f $opsumtmp $conflisttmp $chgtmp $devtmp $dialog_script $notestmp return 1 fi elif [ ! -s $chgtmp ]; then # User selected OK without requesting change rm -f $opsumtmp $conflisttmp $chgtmp $devtmp $dialog_script $notestmp return 0 else # User checked something to change and selected OK. # Re-run the appropriate function(s) and reconfirm. for index in `cat $chgtmp` ; do case ${opsettings[$index]} in "Operation" ) if dialog --defaultno --yesno "Changing the operation will return you to the main menu. Proceed?" 6 50 ; then return 1 fi ;; "Media type" ) media "unmount" mediaselect media "mount" unset iname_confirmed getimagename $operation ;; "Backup method" ) methodselect if [[ $bu_method = "auto" ]]; then log "Determining backup method." method_analyze fi ;; "Compression level" ) complvlselect ;; "Image name" ) unset iname_confirmed getimagename $operation ;; "Version" ) get_version ;; "Devices" ) case $operation in "backup" ) deviceselect "multiple" "Select devices to back up" ;; "restore" ) imageselect ;; esac if [[ $bu_method = "auto" ]]; then log "Determining backup method." method_analyze fi ;; "Source device" ) clone_select ;; "Target device" ) case $operation in "clone" ) clone_select ;; "blank" ) blank_select ;; "badblocks test" ) badblocks_select ;; esac ;; "Type of test" ) rwoptselect ;; "Action on success" ) setpostaction ;; "Notes" ) get_notes ;; esac done rm -f $opsumtmp $conflisttmp $chgtmp $devtmp $dialog_script $notestmp fi done } # ------------------ NETWORK FUNCTIONS ---------------- function check_route() { # Wait until a route is established. # Takes two arguments, the remote ip address, and the number of attempts iptocheck=$1 rc_timeout=$2 no_route=1 unset rc_timesup rc_tries=0 while [ $no_route -a ! $rc_timesup ]; do sleep 1 if ping -q -c1 $iptocheck >/dev/null 2>&1 ; then unset no_route else let "rc_tries +=1" fi if [ "$rc_tries" -eq "$rc_timeout" ]; then rc_timesup=1 fi done if [ $no_route ]; then return 1 ;else return 0 ; fi } function ip_test() { # tests a string to see if it's in a valid IP address format # Argument is an IP address string. Returns 0 if valid, 1 if not ip_regex="[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}" if echo $1|grep -q -e $ip_regex ; then return 0; else return 1; fi } function auto_net_setup() { # Use DHCP to get an IP address local dhcpcdtmp=`mktemp -q` # Check for and unmount any stale mounts if mount|grep -q $MEDIA_MNT_DIR ; then umount -f $MEDIA_MNT_DIR fi # Build array of interfaces ethifs=( ) for nif in `cat /proc/net/dev|grep eth|cut -d : -f1`; do ethifs=( ${ethifs[@]} $nif ) done if [ ${#ethifs[@]} -eq 0 ]; then pause "Couldn't find any usable interfaces. You may need to use USB media." return 1 fi # Check to see if the network is already up (maybe from a previous run # that terminated abnormally) and bring it down. dhcpcd -q -k >/dev/null 2>&1 for ethif in ${ethifs[@]} ; do ifconfig $ethif 0.0.0.0 down >/dev/null 2>&1 done progreport "Setting up the network." log "Setting up the network." # create a /var/lib/nfs/state file if it doesn't exist echo "0000" > /var/lib/nfs/state # kill netplugd to keep it from interfering killall netplugd >/dev/null 2>&1 # Since sysresccd-1.0.4, we have to manually start nfs /etc/init.d/nfsmount start >/dev/null 2>&1 # Since sysresccd-1.5.8, we have to manually start rpc.statd /etc/init.d/rpc.statd start >/dev/null 2>&1 # Get a DHCP address case $backup_server_type in "RescueBRU Server" ) dhcpcd_args=$DHCPCD_RBRUSERVER_ARGS ;; "any DHCP server" ) dhcpcd_args=$DHCPCD_GLOBAL_ARGS ;; * ) pause "There was a problem determining the backup server type. Aborting." return 1 ;; esac unset carrier_present carrier_attempts=0 while [ ! $carrier_present ] && [ $carrier_attempts -lt $MAX_CARRIER_ATTEMPTS ]; do progreport "Requesting an IP address from $backup_server_type ..." dhcpcd -q -k >/dev/null 2>&1 echo ""> $dhcpcdtmp dhcpcd $dhcpcd_args 2>$dhcpcdtmp if cat $dhcpcdtmp|grep -q "no interfaces have a carrier" ; then dhcpcd -q -k >/dev/null 2>&1 sleep 3 else carrier_present=1 fi let "carrier_attempts +=1" done if [ ! $carrier_present ]; then pause "No ethernet interfaces have a carrier. This probably means the cables are disconnected. Aborting." rm -f $dhcpcdtmp return 1 fi if ! cat $dhcpcdtmp|grep -q acknowledged ; then inform "Couldn't get a DHCP offer from $backup_server_type" "5" log "Couldn't get a DHCP offer from $backup_server_type" dhcpcd -q -k >/dev/null 2>&1 rm -f $dhcpcdtmp return 1 fi progreport "...ok." remote_addr=`cat $dhcpcdtmp|grep acknowledged|tail -1|rev|cut -d " " -f1|rev` wkg_addr=`cat $dhcpcdtmp|grep acknowledged|tail -1|rev|cut -d " " -f3|rev` interface=`cat $dhcpcdtmp|grep acknowledged|tail -1|rev|cut -d " " -f5|cut -c2-|rev` progreport "Got the address $wkg_addr from the server at $remote_addr" sleep 3 log "Got the address $wkg_addr from the server at $remote_addr" if ! mount $remote_addr:$remote_dir $MEDIA_MNT_DIR >/dev/null 2>&1 ; then pause "Couldn't mount $remote_addr:$remote_dir. Aborting." log "Couldn't mount $remote_addr:$remote_dir. Aborting." dhcpcd -q -k >/dev/null 2>&1 ifconfig $interface 0.0.0.0 down rm -f $formtmp $dhcpcdtmp return 1 fi echo "is allocated and idle.">$MEDIA_MNT_DIR/$wkg_addr umount $MEDIA_MNT_DIR >/dev/null 2>&1 CLIENT_NET_STATUS=1 rm -f $formtmp $dhcpcdtmp return 0 } function manual_net_setup() { # Set up network manually formtmp=`mktemp -q` progreport "" # Check for and unmount any stale mounts if mount|grep -q $MEDIA_MNT_DIR ; then umount -f $MEDIA_MNT_DIR fi # Build array of interfaces ethifs=( ) for nif in `cat /proc/net/dev|grep eth|cut -d : -f1`; do ethifs=( ${ethifs[@]} $nif ) done if [ ${#ethifs[@]} -eq 0 ]; then pause "Couldn't find any usable interfaces. You may need to USB media." rm -f $formtmp return 1 fi # Check to see if the network is already up (maybe from a previous run # that terminated abnormally) and bring it down. dhcpcd -q -k >/dev/null 2>&1 for ethif in ${ethifs[@]} ; do ifconfig $ethif down >/dev/null 2>&1 done progreport "Setting up the network." log "Setting up the network." # create a /var/lib/nfs/state file if it doesn't exist echo "0000" > /var/lib/nfs/state # kill netplugd to keep it from interfering killall netplugd >/dev/null 2>&1 # Since sysresccd-1.0.4, we have to manually start nfs /etc/init.d/nfsmount start >/dev/null 2>&1 # Since sysresccd-1.5.8, we have to manually start rpc.statd /etc/init.d/rpc.statd start >/dev/null 2>&1 # prompt for IP info addr_invalid=1 remote_dir_invalid=1 while [ "$addr_invalid" -o "$remote_dir_invalid" ]; do dialog --form "Backup Server Network Parameters:" 10 77 4 \ "Backup server IP address:" 1 1 "$NFS_REMOTE_ADDR_DEF" 1 32 32 15 \ "Client initial IP address:" 2 1 "$NFS_SUBNET_DEF.$NFS_LOCAL_DEF" 2 32 32 15 \ "Start of client address block:" 3 1 "$NFS_SUBNET_DEF.$NFS_CLIENT_START_DEF" 3 32 32 15 \ "Remote directory:" 4 1 "$NFS_REMOTE_DIR_DEF" 4 32 32 255 \ 2>$formtmp formdata=( `cat $formtmp` ) if [ ${#formdata[@]} -eq 0 ]; then pause "Manual net setup cancelled." log "Manual net setup cancelled." rm -f $formtmp return 1 fi remote_addr=${formdata[0]} initial_addr=${formdata[1]} local_blk_start=${formdata[2]} remote_dir=${formdata[3]} if ip_test "$remote_addr" ; then unset addr_invalid else pause "Backup server IP address is not a valid IP address. Try again." fi if ip_test "$initial_addr" ; then unset addr_invalid else pause "Client initial IP address is not a valid IP address. Try again." fi if ip_test "$local_blk_start" ; then nset addr_invalid else pause "Start of client address block is not a valid IP address. Try again." fi if [ "$remote_dir" != "" ]; then unset remote_dir_invalid else pause "Remote directory cannot be blank. Try again." fi done # Scan interfaces for server interface="unknown" progreport "Scanning network interfaces for connection to backup server..." qs_text="Performing quick scan..." ns_text="Performing normal scan..." ls_text="Performing long scan..." xs_text="Performing extra-long scan..." scanpass=0 while [ "$interface" = "unknown" -a "$scanpass" -lt 4 ]; do let "scanpass +=1" case $scanpass in 1 ) scanpings=1 scan_info_text="$scan_info_text \n $qs_text" ;; 2 ) scanpings=5 scan_info_text="$scan_info_text \n $ns_text" ;; 3 ) if ! dialog --no-label "Abort" --no-ok --defaultno \ --pause "No server found yet. Will keep trying. Press enter to abort." 0 0 10 ; then scanpass=9 break else scanpings=10 scan_info_text="$scan_info_text \n $ls_text" fi ;; 4 ) scanpings=20 scan_info_text="$scan_info_text \n $xs_text" ;; esac for ifname in ${ethifs[@]} ; do progreport "$scan_info_text $ifname ..." ifconfig $ifname $initial_addr netmask 255.255.255.0 sleep $scanpings if ping -q -c $scanpings -I $ifname $remote_addr >/dev/null 2>&1 ; then interface=$ifname break ifconfig $ifname down sleep 1 fi done done if [ "$interface" = "unknown" ]; then pause "Couldn't find a server on any interface. Make sure server is running and all cables are connected." log "Couldn't find a server on any interface." rm -f $formtmp ifconfig $ifname down return 1 else inform "Found a server on the $interface interface." log "Found a server on the $interface interface." fi # Determine what my real address will be case $scanpings in 1 ) inform "Using quick setup based on interface speed." ;; 5 ) inform "Using normal setup based on interface speed." ;; 10 ) inform "Using slow setup based on interface speed." ;; 20 ) inform "Using extra-slow setup based on interface speed." ;; esac initial_ha=`echo $initial_addr|cut -d "." -f4` backup_subnet=`echo $remote_addr|cut -d "." -f1-3` ha=`echo $local_blk_start|cut -d "." -f4` while ping -q -c $scanpings -I $interface $backup_subnet.$ha >/dev/null 2>&1 && [ $ha -lt $initial_ha ]; do let "ha += 1" progreport "Trying $backup_subnet.$ha..." done if [ $ha -eq $initial_ha ]; then pause "Unable to get a working IP address. Too many clients may be connected." log "Unable to get a working IP address." rm -f $formtmp ifconfig $ifname down return 1 else inform "$backup_subnet.$ha is available." wkg_addr=$backup_subnet.$ha fi ifconfig $interface down sleep 1 ifconfig $interface $wkg_addr netmask 255.255.255.0 sleep $scanpings if ! check_route "$remote_addr" $ROUTE_TIMEOUT; then pause "Couldn't establish a route to $remote_addr after $ROUTE_TIMEOUT attempts. Aborting." log "Couldn't establish a route to $remote_addr after $ROUTE_TIMEOUT attempts. Aborting." ifconfig $interface down rm -f $formtmp return 1 fi if ! mount $remote_addr:$remote_dir $MEDIA_MNT_DIR >/dev/null 2>&1 ; then pause "Couldn't mount $remote_addr:$remote_dir. Aborting." log "Couldn't mount $remote_addr:$remote_dir. Aborting." ifconfig $interface down rm -f $formtmp return 1 fi echo "is allocated and idle.">$MEDIA_MNT_DIR/$wkg_addr umount $MEDIA_MNT_DIR >/dev/null 2>&1 CLIENT_NET_STATUS=1 log "Backup server IP address is $remote_addr" log "Local IP address is $wkg_addr" log "Remote directory is $remote_dir" rm -f $formtmp progreport "" return 0 } # ------------------ MEDIA FUNCTIONS ------------------ function media() { # Dispatcher for media mounting/unmounting. Chooses correct media type function # parameter is "mount" or "unmount" if [ ! -d $MEDIA_MNT_DIR ]; then mkdir $MEDIA_MNT_DIR fi case $media_type in "NFS" ) NFS "$1" ; return $? ;; "USB" ) USB "$1" ; return $? ;; "Local" ) Local "$1" ; return $? ;; * ) pause "Must specify mount or unmount." return 1 ;; esac } function NFS() { # Mount or unmount NFS. # parameter is "mount" or "unmount" if [ "$CLIENT_NET_STATUS" -eq 0 ]; then if [ "$netsetup_type" = "auto" ]; then if ! auto_net_setup; then return 1;fi elif [ "$netsetup_type" = "manual" ]; then if ! manual_net_setup; then return 1;fi fi fi if [ "$1" = "mount" ]; then progreport "Please wait while route is established..." if check_route $remote_addr $ROUTE_TIMEOUT ; then progreport "...ok" log "established route to $remote_addr" else flush_input pause "Couldn't establish a route to $remote_addr after $ROUTE_TIMEOUT attempts." log "Couldn't establish a route to $remote_addr after $ROUTE_TIMEOUT attempts. Aborting." return 1 fi if grep -q $MEDIA_MNT_DIR /etc/mtab ; then progreport "Remote directory appears to be already mounted. Continuing." log "Remote directory appears to be already mounted. Continuing." else progreport "Mounting remote directory..." if mount $remote_addr:$remote_dir $MEDIA_MNT_DIR -onolock ; then progreport "...ok" log "Remote directory mounted." else progreport "... FAILED" sleep 4 flush_input pause "Couldn't mount remote NFS directory. Ensure NFS server is running on $remote_addr." return 1 fi fi nfs_media_free_txt=`df -h $MEDIA_MNT_DIR|grep "$MEDIA_MNT_DIR"|awk '{print $4}'` nfs_media_free=`df -B 1024k $MEDIA_MNT_DIR|grep "$MEDIA_MNT_DIR"|awk '{print $4}'` if [ $nfs_media_free -lt $MIN_MEDIA_FREE ]; then if ! approved "There is less than 2G available on the backup media. If you are running a backup, you may run out of space. If you know what you're doing, you can continue anyway, or abort the operation. Continue?" "n"; then inform "Aborting. Unmounting remote directory..." log "Aborting. Unmounting remote directory." sync umount $MEDIA_MNT_DIR sync return 1 fi fi progreport "Remote directory $remote_dir/$BACKUP_DIR mounted with $nfs_media_free_txt free space." sleep 3 log "Remote directory $remote_dir/$BACKUP_DIR mounted with $nfs_media_free_txt free space." return 0 elif [ "$1" = "unmount" ]; then progreport "Unmounting remote directory..." log "Unmounting remote directory." sync umount $MEDIA_MNT_DIR sync progreport "... done" return 0 else pause "Must specify mount or unmount." return 1 fi } function USB() { # Prompt user to plug in USB drive and mount it # parameter is "mount" or "unmount" trying=1 local infotext="Searching for USB drive" flush_input if [ "$1" = "mount" ]; then while [ $trying ]; do infotext="$infotext ." dialog --begin 3 5 --infobox "Plug in USB drive now or press any key to abort. If drive is already plugged in, and is not detected, unplug it, wait a few seconds, and plug it in again." 0 0 \ --and-widget \ --begin 10 5 --sleep 1 --infobox "$infotext" 14 70 if read -s -n1 -t1 kp <&1 ; then pause "USB media mounting aborted." log "USB media mounting aborted." return 1 fi if tail -2 /var/log/messages|grep -q SCSI ; then unset trying fi done usbdvc=`tail -2 /var/log/messages|grep SCSI|grep -o -e "sd[a-z]"` usbparts=`ls /dev/$usbdvc?` # Add USB device to the exclude list exclude_dev "/dev/$usbdvc" # add USB partitions to exclude list for uprt in $usbparts; do exclude_dev $uprt; done inform "Found USB drive as /dev/$usbdvc" log "Found USB drive as /dev/$usbdvc" mount /dev/$usbdvc"1" $MEDIA_MNT_DIR usb_media_free=`df -B 1024k $MEDIA_MNT_DIR|grep "$MEDIA_MNT_DIR"|awk '{print $4}'` if [ $usb_media_free -lt $MIN_MEDIA_FREE ]; then if ! approved "There is less than 2G available on the backup media. If you are running a backup, you may run out of space. If you know what you're doing, you can continue anyway, or abort the operation. Continue?" "n"; then inform "Aborting. Unmounting USB media..." log "Aborting. Unmounting USB media." sync umount $MEDIA_MNT_DIR sync inform "Aborting. Unmounting remote directory... done" inform "It is now safe to unplug the USB drive." return 1 fi fi log "USB mounted with $usb_media_free M free space." return 0 elif [ "$1" = "unmount" ]; then inform "Unmounting USB drive..." sync umount $MEDIA_MNT_DIR sync inform "Unmounting USB drive...done." "1" inform "It is now safe to unplug the USB drive." return 0 else pause "Must specify mount or unmount." return 1 fi } function Local() { # Prompt user for local pathname # parameter is "mount" or "unmount" if [ "$LOCAL_STATUS" -eq 0 ]; then if ! localdir_setup ; then return 1;fi fi if [ "$1" = "mount" ]; then IMAGE_PATH=$local_dir return 0 elif [ "$1" = "unmount" ]; then sync return 0 else pause "Must specify mount or unmount." return 1 fi } function localdir_setup() { # Determine correct local directory localdirtmp=`mktemp -q` path_invalid=true while [ $path_invalid ]; do flush_input if dialog --inputbox "Enter full path of backup directory" 8 78 "$LOCAL_DIR" 2>$localdirtmp ; then local_dir=`cat $localdirtmp` if [ "$local_dir" = "" ]; then local_dir=$LOCAL_DIR fi if [ -d "$local_dir" ]; then unset path_invalid else pause "Specified directory does not exist. Perhaps you made a typo.\nTry again." fi else pause "Local directory setup cancelled." log "Local directory setup cancelled." rm -f $localdirtemp return 1 fi done LOCAL_STATUS=1 log "Local backup directory is $local_dir" rm -f $localdirtemp return 0 } function createdir() { # Create backup directory while avoiding accidental overwrites unset finalimgname if [ ! -d "$IMAGE_PATH" ]; then mediasetup fi if [ $imgovr -eq 1 ]; then finalimgname=$imagename if [ -d "$IMAGE_PATH/$imagename.new" ]; then rm -rf $IMAGE_PATH/$imagename.new fi imagename="$imagename.new" fi inform "Creating backup directory $IMAGE_PATH/$imagename..." "2" if ! mkdir $IMAGE_PATH/$imagename ; then pause "An error was encountered while creating backup directory. Aborting." log "An error was encountered while creating backup directory. Aborting." return 1 else inform "Creating backup directory $IMAGE_PATH/$imagename... done." log "Backup directory is $IMAGE_PATH/$imagename." # Copy any previous log/note files to new backup log "Copying previous log/note files to new backup." if ls $IMAGE_PATH/$finalimgname/*.log >/dev/null 2>&1 ; then cp -f $IMAGE_PATH/$finalimgname/*.log $IMAGE_PATH/$imagename/ fi if ls $IMAGE_PATH/$finalimgname/*.notes >/dev/null 2>&1 ; then cp -f $IMAGE_PATH/$finalimgname/*.notes $IMAGE_PATH/$imagename/ fi return 0 fi } function mediasetup() { # Set up backup media if [ "$CLIENT_NET_STATUS" -eq 1 ]; then echo "is allocated and is performing media setup.">$MEDIA_MNT_DIR/$wkg_addr fi if [ -d $IMAGE_PATH ]; then pause "Backup media appears to be already set up." flush_input if dialog --aspect 80 --defaultno \ --yesno "CONTINUING WITH MEDIA SETUP WILL DELETE ANY BACKUPS STORED ON MEDIA! Select Yes to continue, or No to abort :" 0 0 ; then inform "Removing old image path..." rm -rf $IMAGE_PATH sleep 3 inform "Removing old image path...done." sleep 1 inform "Syncing drive..." sync sleep 3 inform "Syncing drive...done." sleep 1 inform "Creating new image path..." mkdir $IMAGE_PATH sleep 3 inform "Creating new image path...done." sleep 1 inform "Syncing drive..." sync sleep 3 inform "Syncing drive...done." inform "Media setup complete." else pause "Media setup aborted!" if [ "$CLIENT_NET_STATUS" -eq 1 ]; then echo "is allocated and idle.">$MEDIA_MNT_DIR/$wkg_addr fi return 1 fi fi inform "Creating new image path..." mkdir $IMAGE_PATH sleep 3 inform "Creating new image path...done." sleep 1 inform "Syncing drive..." sync sleep 3 inform "Syncing drive...done." inform "Media setup complete." if [ "$CLIENT_NET_STATUS" -eq 1 ]; then echo "is allocated and idle.">$MEDIA_MNT_DIR/$wkg_addr fi return 0 } function main() { # Main program function # Fix the font so dialog boxes aren't corrupt /usr/bin/setfont # Initialize variables to defaults defaultsettings # Check to see if we were called by a rbruserver # first parameter must be -localserver # second parameter is the name of the local directory # third and subsequent parameters are devices to exclude while doing a backup if [ "$1" = "-localserver" ]; then num_of_args=$# LOCAL_SERVER_RUNNING=1 shift let "num_of_args -= 1" LOCAL_DIR=$1 LOCAL_STATUS=1 local_dir=$LOCAL_DIR media_type="Local" shift let "num_of_args -= 1" while [ $num_of_args -gt 0 ]; do exclude_dev $1 shift let "num_of_args -= 1" done fi # Create a tempfile for global progress reports progfile=`mktemp -q` # Remove any existing temporary log file and start a new one rm -f $LOG_PATH/* log "RescueBRU $PROG_VERSION" log "Run started `date`" while true; do mainmenu case $mmchoice in "p" | "P" ) log "\"Exit and power down\" chosen from main menu." shutdown_proc "shutdown" reset ;; "r" | "R" ) log "\"Exit and reboot\" chosen from main menu." shutdown_proc "reboot" reset ;; "s" | "S" ) log "\"Exit to shell\" chosen from main menu." log "Run finished." if [ $LOCAL_SERVER_RUNNING ]; then rm -f $progfile dialog --sleep 3 --infobox "Returning to RescueBRU Server" 3 40 exit 0 fi rm -f $progfile inform "Exiting to shell" "2" if [ "$CLIENT_NET_STATUS" -eq 1 ]; then if ! check_route $remote_addr $ROUTE_TIMEOUT ; then inform "Couldn't establish a route to $remote_addr after $ROUTE_TIMEOUT attempts. Aborting" log "Couldn't establish a route to $remote_addr after $ROUTE_TIMEOUT attempts. Aborting." umount -f $MEDIA_MNT_DIR /etc/init.d/nfsmount stop >/dev/null 2>&1 dhcpcd -q -k >/dev/null 2>&1 ifconfig $interface 0.0.0.0 down return 1 fi mount $remote_addr:$remote_dir $MEDIA_MNT_DIR rm -f $MEDIA_MNT_DIR/$wkg_addr umount $MEDIA_MNT_DIR /etc/init.d/nfsmount stop >/dev/null 2>&1 dhcpcd -q -k >/dev/null 2>&1 ifconfig $interface 0.0.0.0 down fi reset exit 0 ;; "?" ) log "\"Help\" chosen from main menu." showhelp ;; 7 ) log "\"Show backups available on media\" chosen from main menu." if media "mount" ; then if [ "$CLIENT_NET_STATUS" -eq 1 ]; then echo "is allocated and is viewing images.">$MEDIA_MNT_DIR/$wkg_addr fi showimages if [ "$CLIENT_NET_STATUS" -eq 1 ]; then echo "is allocated and idle.">$MEDIA_MNT_DIR/$wkg_addr fi media "unmount" else pause "Could not mount backup media. Unable to show backup images." fi ;; 6 ) log "\"View logs or notes\" chosen from main menu." if media "mount" ; then lognoteview media "unmount" else pause "Could not mount backup media. Unable to view logs." fi ;; 5 ) log "\"Image management\" chosen from main menu" log "Media type is $media_type" do_image_mod ;; 4 ) log "\"Disk Utilities\" chosen from main menu" log "Media type is $media_type" if ! diskutil ; then pause "A disk utility failed." fi ;; 3 ) log "\"Do a restore\" chosen from main menu." log "Media type is $media_type" if ! do_restore ; then pause "Restore failed." fi ;; 2 ) log "\"Do a backup\" chosen from main menu." log "Media type is $media_type" log "Backup method is $bu_method" if ! do_backup ; then pause "Backup failed." fi ;; 1 ) log "\"Settings\" chosen from main menu." settings ;; esac done } main "$@" exit $?