Files
RescueBRU/customcd/files/root/rescuebru/rescuebru.sh
2026-03-03 19:53:38 -05:00

3445 lines
124 KiB
Bash

#!/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 <Submit> or <No notes> 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,
# "<fstype> 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 $?