I expect that other folks here have the same problem I do: family orfriends that could use the rescueCD to do backups but have no hope of runningit as it stands. Enter simplebackup which is a perl script designed to autorunin a customized copy of the rescueCD. It uses dialog and pre configuration(which you will need to do!) to make a backup as simple as boot the CD andwait for dialog to come up then select backup and press enter. Assuming noerrors, the Windows system will be backed up and then the system will shut down.If something does go wrong the script will tell the user to contact theirsupport person (who is likely you!) and has (assuming it found a backupstore)logged everything that it did to a log file for you. Under limited conditions(mostly the same disk that the backup was done from) it will also do a restoreof a previous version (it by default backs up the current system first to avoiddata loss) so the user can recover from downloading something they shouldn'thave (or a problem from Windows update).

Design principles are:

EKISS: (Externally, Keep It Simple Stupid), internally it isn't so simple but as noted externally it is menu driven and requires the user to know nothing other than they want to backup (or hopefully rarely, restore) their system.

Be CD friendly: that means configuration can't be in the script as the file system is read only. Thus configuration is done via zero length text files inserted in to the Windows file systems. The script mounts the file systems and searches for the files to identify what disks / partitions it needs.

Have an External backupstore: In my case (and probably yours too) that is a USB connected 500gig to 4 terabyte USB drive that holds the backup files and can be moved off site if desired for fire safety. It needs to be formatted ntfs (not fat) because of the 2 gig fat file system limitation (backups are 15 gigabytes or larger!). It too is identified to the system by having a zero length text file in its root file system.

Do as little as possible on the rescueCD: Things like splitting the backups in to less than 4 gig chunks and writing them to dvd for archiving can and should be done in Windows, the backup script only needs to backup, restore and do simple backupstore maintance while booted from Linux, so thats all it does.

So to try this out the first thing you will need to do is recreate thesimplebackup.pl script into a single file by cutting and pasting the 3 or soposts (to obey the 60k character post limit :-)) that the script has beendivided into (as the script is currently 140,000+ characters) then:

1) Prepare a backupstore. Typically an external USB disk drive. I use a 500Gig Seagate. The drive needs to be reformatted to ntfs (for larger than 2 gig files) and needs a file systemresccd_backupstore.txt (which can be empty, its existance is what's important) in the root directory to mark this as the backupstore drive.

2) Install a file systemresccd_windows.txt (which can also be empty) in the root of the Windows c: drive to mark the windows drive/partition (note that by default the script will find and mark other partitions on the system disk for itself such as a boot and reserved / recovery partitions).

3) Plug in the backupstore drive on the windows system then boot from the rescue CD (or if you like from the backupstore drive). From a terminal window run simplebackup.pl and follow the prompts (and/or deal with the error messages :-)).

4) The windows system will be backed up (assuming everything is configured correctly) and will then shut down (changing $BACKUP_END = "shutdown"; to $BACKUP_END = "prompt"; in the perl script will bring you back to the command prompt for testing).

The backup will appear in a directory on the backupstore that lookslike this:

which are the various files from the backup. The odd looking filename schemeis in fact a primitive database encoding SB for simple backup, the date andtime of the backup, and an optional user comment. The backup files (the oneswith a leading #) include the partition type, partition number (except forthe MBR which doesn't have a number), the disk serial number of the disk theycame from and an appropriate file extension to tell you what tool to use tomanually restore them if needed. At the end are the 3 log files:

/var/log/messages

from linux (to find hardware errors or boot problems mostly)

/var/log/perl.log

the console output from perl for perl errors

demo.log

the simplebackup log file which contains a listing of all the commands the perlscript executed and what the user requested so you can see after the factwhat went on if something goes wrong. Grepping for "error" and "warning"in this file is a good bet to see if problems occurred.

By default the script will only restore a file to the same disk serialnumber it came from. In the case of a disk failure you would need to manuallyrestore the backup files to a new disk. The purpose of this is that a singlebackupstore can be used to backup and more importantly restore safely multiplemachines (such as a desktop and a laptop) and will present the user with onlythe restore options for the correct machine preventing restoring an OS thatwon't work on to a machine. When things are working correctly you can then create a custom CD toautomate the backup. I use my backupstore as my development and boot device.To do so I have the following layout:

(at present the nodhcp etc. options don't work but they should and are desirable for security. Simplebackup doesn't need and shouldn't have network access as most folks don't have firewalls I don't think).

5) vi /mnt/custom/customcd/isoroot/boot/grub/grub-471.cfg and replace the default boot entry with

(again this doesn't actually appear to work, the nomodeset and vga=796 appear to be ignored. The script still works, just the screen is small and looks odd because the resolution is incorrect, thats life!). This appears to be for some UEFI boots. It looks like it will only work for 64 bit machines as well.

6) Create a new iso using isogen and burn it (or copy it to partition 1 of the backupstore drive) and away you should go.

If you have questions, post them here and as I remember I'll try andanswer. Since the script works for me I'm unlikely to do much else with it butyou should feel free to both thank the folks that make the rescueCD (as Icertainly do!) for the excellent product that makes this script possible andpost any fixes or improvements to the script you make here.

# All rights reserved.## Redistribution and use in source and binary forms, with or without# modification, are permitted provided that the following conditions are met:

# 1. Redistributions of source code must retain the above copyright notice,# this list of conditions and the following disclaimer.

# 2. Redistributions in binary form must reproduce the above copyright notice,# this list of conditions and the following disclaimer in the documentation# and/or other materials provided with the distribution.

# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE# POSSIBILITY OF SUCH DAMAGE.

# Aimed at:

# The casual Windows user (family, friends) that isn't Linux or even# very computer literate but want to be able to backup and perhaps restore# their system for themselves. That won't always be possible, when things go# wrong this script will tell the user to "Seek support" which is probably you,# or someone else that can run the tools on the Rescue CD. This script# will hopefully have saved enough information about whats going on to let# you recover somehow. For instance before allowing the user to restore, it# will take a current backup of the system partitions and MBR which should# prevent accidental data loss during restore. This is intended for a custom# CD or USB key installation that will boot directly in to this script to make# it easy to use. It needs to do docache so the user can remove the USB key or# the CD during the backup (by default it will shutdown, but it can be set to# either reboot or stop at a dialog prompt when it finishes) so a reboot will# bring windows back up normally. The script keeps a log of all actions# performed (and return codes) and saves a copy of /var/log/messages from# Linux on the backupstore so you can see hardware type issues (disk errors# etc.) from the backup.

# Assumptions:

# Backups stored on an external USB hard drive (the backupstore). I have 500gig# Seagate and Western Digital units reformatted from FAT (which won't store# a bigger than 2 gig file) to NTFS (which will take the 50+ gigs of# Windows 7 Home premium (blotted pig that it is :-)), XP is around 10 to 15# gigs) neither will fit in a fat file system without being split. Nothing# stopping you using a second internal SATA drive (it is in fact faster) but# the external can be taken off site for backup and thus is better. An internal# SATA or IDE disk with the backup then split and written to CD for offsite# storage would do as well.

# Disks (backup and backupstore) identified by a file in the root file system# $BACKUPSTORE_FILE (defined below) for the backupstore drive, $WINDOWS_FILE# for the system disk, $BOOT_FILE for the boot partition and some others. This# allows user configuration without changes to the bootable CD image (in fact# more configuration than is currently implemented is easily possible, just# read the contents of one or more of those files from the file system and# change configuation as desired. Again I currently don't have the need and# thus haven't done it). These files end in ".txt" so a Windows only user can# create a $WINDOWS_FILE on the Windows C: drive using notepad and a# $BACKUPSTORE_FILE on a suitably formatted USB drive for the backupstore and# the system will find and tag the boot partition and any reserved partitions# on the system disk for itself.# Because backup image names contain the model and serial number# of the disk they came from, and restore will only offer restore images from# the current disk, it is possible to back up more than one machine on a single# backupstore (a desktop and a laptop for instance).

# Restore requires that the system disk MBR/VBR (and thus the partition data)# be identical to the backup and the disk model number / serial number be the# same for the user to be able to restore. If either are different the restore# will abort and tell the user to seek support (i.e. you or someone else that# can run the System Rescue CD tools :-)).# The obvious case this doesn't cover is system disk failure, but this# is a simple backup aimed at the non technical user and restoring to a new# disk that needs partitioning and formatting is best left to someone familiar# with the tools. It would be fairly easy to restore the saved MBR/VBR to# a new disk of the same model number (but different serial number), run parted# to format it, then restore from the backup but I haven't done that. I think# disk failures are rare enough for this to be acceptable (YMMV). Note that# Windows can be picky about the geometry of a new drive if it is a different# layout than the original, and thus may not boot even with a successful# restore. Windows also requires the VBR (the 63 reserved sectors after the# MBR) to boot so the script saves that as well (but you get to restore it# with dd :-)). Even then you should be able to mount (if not boot) the disk# to at least get the files off of it.

# Start of actual scipt

# User setable parameters:

# The name of this script, so that if the user were to type that at the shell# prompt this script will restart.

$SCRIPT_NAME = "simplebackup.pl";

# If $DESCRIPTION is set to "y", then backup will prompt the user for a# text description of the backup. If it is set to "n" the backup will proceed# without any further user intervention as soon as backup is selected in the# menu.

$DESCRIPTION = "y";

# You can keep a copy of the MD5 checksum of the backup image file and check# it during restore, but with this implementation it about doubles the time# that backup and restore take. The proper answer is to modify ntfsclone to# calculate and output the MD5 of the image files that it processes (but I# so far haven't done so). This code is here but disabled by default for# taking too much time. Uncommenting the next line will enable it (and about# double the time the backup takes!).

$DO_MD5 = 0; # enable MD5 checks (1). Default: disabled

# By default the script will backup the system before doing a restore# (to insure that you have a copy to recover from if the restore was a mistake!)# This of course adds time to the restore so if you comment out this line that# step will be skipped (but you have no insurance copy either!).

# Chose what will happen at the end of backup. By default the system will# put up a success screen and wait for user acknowledgement. You can also# set it to shutdown or reboot (presumably to windows by removing the rescueCD# media) after the backup or restore.

$BACKUP_END = "prompt"; # options "prompt" to come back to a prompt # to allow the user to select what happens next. # "shutdown" to power off after a successfull # backup (errors will cause a prompt until there # is user acknowlegement of the error). # "reboot" to reboot to Windows after the # backup (presuming the rescueCD media was # removed.) On error the system will wait at # a prompt til the user acknowledges the error.

$RESTORE_END = "prompt"; # options "prompt" to come back to a prompt # to allow the user to select what happens next.# "shutdown" to power off after a successfull # restore (errors will cause a prompt until # there is user acknowlegement of the error). # "reboot" to reboot to Windows after the # restore (presumably to Windows if the rescue # CD media is removed.) On error the system # will wait at a prompt til the user # acknowledges the error.

# Typically the swap file is a) large, and b) useless in the backup so by# default we will truncate the file /pagefile.sys in the Windows partition# (Windows will happily restore it to its huge size as part of boot) to save# the useless backup time (my pagefile.sys is around 5 gigabytes which saves# a significant amount of backup time). In the case where this is undesirable# for some reason commenting out the line below will stop backup from# truncating the file.

# Auto detect and tag Windows reserved and boot partitions without the text# files below in the filesystem. There may be some cases when this interferes,# so commenting out the line below will disable this feature (but the system# eserved and boot partitions (except msftres partitions) won't be backed up or# restored unless you make sure the appropriate text file is in the file# system ...).

$AUTO_DETECT = 1; # Autodetect reserved. Default: enabled 0 disables

# Keep /var/log/messages only during the backup (deletes the boot messages!)# Originally this was the default, but the file size of the complete syslog# file is negligable compared to the backup files so the current default is# to keep the entire syslog. However since the code was there, keeping only# the syslog data during the backup or restore can be set by setting the value# to 1 from 0

# Label of ntfs volume where images will be stored (must be ntfs or other# filesystem capable of storing > 2 gig files i.e. not FAT!).# Create this as a file (0 length is fine) in the root of the ntfs file system# so we can find it and identify the backup store disk. If more than one such# disk is found the one with the most free space will be used. Doing this means# the CD doesn't need to change, the user creates files in their file systems# they want backed up to do the necessary configuration. We also can (but don't# currently) set other configuration options from this file as it resides on# a user editable hard disk. The .txt ending makes creation on Windows via# notepad easy for user configuration. Given the encryption viruses running# around these days making this file system ext3 or ext4 may be a good bet# but it isn't currently done, only ntfs amd ext would need script code changes.

$BACKUPSTORE_FILE = "systemresccd_backupstore.txt";

# disk label (as above) of the Windows system volume to be backed up.# There should be only one system volume, defining two will cause an error# response. The .txt ending makes creation on Windows via notepad easy for user# configuration.

$WINDOWS_FILE = "systemresccd_windows.txt";

# Data volume (other than the system disk) to be backed up.

$BACKUP_FILE = "systemresccd_back_me_up.txt";

# As of Windows 7 there is a 100 meg boot partition (its possible that the VBR# is no longer used). It isn't accessable from Windows and thus the script will# auto detect it. For cases when auto detect is undesirable, the following file# should be placed in the partition's / file system from the rescuecd# (autodetect writes this file if it isn't present and autodetect is running).# For systems with an msftres file system (containing the keys), it will be# automatically marked and backed up as you can't mount it to add or read files.

$BOOT_FILE = "systemresccd_boot_part.txt";

# Various OEM copies of windows have reserved partitions (usually to restore# a corrupted system) also not accessable from Windows and autodetected.# For cases when auto detect is undesirable, the following file should be# placed in the partition's / file system from the rescuecd (autodetect writes# this file if it isn't present and autodetect is running).

$RESERVED_FILE = "systemresccd_reserved_part.txt";

# Directories and files to identify a Windows system disk to verify the# backup file is on the correct partition. This may vary by windows version# and thus may need changes ...

# Number of blocks to add to the size of the used block count on the CLONE# partition to decide if the BACKUPSTORE has enough space to do the backup# and store the log files generated. Err on the side of too much space# available for safety, you really don't want to run out of disk space after# a half hour of backing up ...

$OVERHEAD = 1000;

# dialog formatting settings.

# Backtitle message displayed on dialog screens (top line can only be 1 line).

$DIALOG_RADIO_RESTORE = 12; # Height radio box select window for restore # (will scroll if more than 12 are present)

#$DEBUG = 1; # enable debug printouts for testing.

# End of user configuration section.

# absolute path to the various programs (may change with new software versions# and/or different base OSes) as this script is running as root and we want# to avoid path based attacks, specify the full path to the programs.

# Arrange to trap the int and quit signals to dismount mounted file systems# first before exiting.

$SIG{'INT'} = 'interrupt_handler';$SIG{'QUIT'} = 'interrupt_handler';

# Check for the existance as a file for all the programs from the rescueCD in# use. This should flag changes in the location of binaries which has happened# with different rescueCD versions in the past.

# Filled in from the backupstore with the most free space available (if there# is more than one backupstore present, which will cause a warning).

$backupstore_drive_part = ""; # backupstore drive_partition

$backupstore_space = ""; # Available space in 1K blocks

$backupstore_percent = ""; # %full from a df

# Indicate the backupstore isn't yet mounted on /mnt/backup. Once it is we need# to unmount it before exiting.

$backupstore_drive_mounted = "n";

# Amount of space in 1K blocks needed to backup the selected partitions.

$needed_space = 0;

# Count of system tag files found (should be 1).

$system_tag_files_found= 0;

# Count of boot tag files found (should be 1).

$boot_tag_files_found = 0;

# Count of backupstore tag files found (should be 1)

$backupstore_tag_files_found = 0;

# Flag indicating warning messages have been written to the log file.

$warn = "n";

# The drive/partition of the system partition.

$sys_drive_part = "";

# A flag indicating that the system partition is also the boot partition.

$sys_bootable_no_tag = "n";

# The type (msdos or gpt) of the system drive partition table.

$sys_mbr_type = "";

# Flag indicating the /mnt/windows file system is mounted.

$mnt_windows_mounted = "n";

# The current backup directory name.

$dir_name = "";

# flag indicating the log file is open.

$log_open = "n";

# end of global variables.

open(SYSLOG, "/var/log/messages");

if ($? != 0) {

$err_msg = "$prog: Error: Can't open /var/log/messages $! ($?)\n";

&unrecoverable_error($err_msg);}

if ($SYSLOG_DURING_OP_ONLY == 1) {

# Remember the starting position of /var/log/messages to copy what # happens during backup to the backup log later. At this point there # is a ream of OS dependent stuff from boot in the file that we # typically don't care about. We mostly care about things that happen # (such as I/O errors) while our backup is running (or at least I do). # The various exit subroutines use this to copy from this point to # EOF in /var/log/messages to a log file on the BACKUPSTORE drive so # you can see it after the rescuecd system is rebooted and the in # memory copy goes away. It basically seeks to EOF on /var/log/messages # right now and remembers that position as a starting point later.

$res = seek(SYSLOG, 0, 2);

} else {

# set the start position to start of file so we save the entire syslog # file to the backupstore.

$res = seek(SYSLOG, 0, 0);}

# Set the syslog file current position so we only save the log data once in the# case of multiple operations that cause a log file save.

$log_cur = tell(SYSLOG);

close (SYSLOG);

# Make sure /mnt/backup and /mnt/windows are not mounted (as they shouldn't be)# and unmount them if they are mounted. This is mostly for development when# I have /mnt/backup mounted before starting the script on a real boot it# shouldn't ever occur.

if (&is_mounted("/mnt/backup")) {

# Set up to log the partition information that we dismounted. # on the assumption it will succeed.

$prelog_msgs .= "$prog: Warning: unmounted /mnt/backup\n";

# This won't return if the unmount isn't successful!

&unmount("/mnt/backup");}

if (&is_mounted("/mnt/windows")) {

# Set up to log the partition information that we dismounted. # on the assumption it will succeed.

$prelog_msgs .= "$prog: Warning: unmounted /mnt/windows\n";

# This won't return if the unmount isn't successful!

&unmount("/mnt/windows");}

# Main command loop. The user will need to select an exit to get out of here# (that can be reboot, shutdown or escape to a Linux shell to run tools).# We need to close the log file, copy /var/log/messages and dismount the# backupstore file system before exiting. As a result interrupts are trapped# so a cntrl-c will close any open files before exiting.

# indicate that we don't yet have any disks detected (setting this back to "n"# later will cause a rescan of the disks which can be usefull when a USB# drive is connected after the fact).

$have_disks = "n";

# Have we issued the warning messages yet? ("y" prevents multiple warnings# about the same problem.

# Use subroutine get_disks() to find all the disks linux has # discovered. It won't return if a fatal error occurs.

%disks_parted = &get_disks();

# If we got here we have a list of disks / partitions so # mount the partitions (only ntfs and fat for now) one at a time # and use df to gather usage data and look for our tag files # indicating a backupstore, a system partition, data partition # or a boot partition. Then dismount them again. It won't return # if a fatal error occurs.

%disks_df = &get_partition_df(%disks_parted);

}

# if we got here we have successfully found the disks so see if we # found a backupstore (as it is the minimum we need to do anything at # all!)

if ($backupstore_drive_part eq "") {

# No, so give the user the bad news and see what they want to # do.

&no_backupstore();

# if we return the user elected to try connecting their USB # connected backupstore again so fall through to rescan the # disks. If the user chooses to reboot or shutdown # no_backupstore won't return.

} else {

if ($have_disks eq "n") {

# We have found a backupstore at least so mark the # disks as scanned and proceed.

$have_disks = "y";

if ($backupstore_drive_mounted eq "n") {

# Try and mount the backupstore filesystem an # error will be fatal so no need to check the # return code.

&mount_backupstore(); }

# Now try and create a backup directory and open a log # file in it. If this fails it will give the user the # option of exiting or retrying again after a minute # when the file name will change.

while ($log_open eq "n") {

&open_log(%disks_parted, %disks_df); }

# We now have all the info about the disks so check it # over and flag any errors found and form a list of # partitions to back up. If a fatal error occurs this # won't return.

%backups = &process_disk_info(%disks_df);

# Issue warnings about no system disk, too many system # disks or backupstores etc. If there is no system disk # this will divert in to maintance mode and not return.

if ($warnings_issued eq "n") {

&issue_warnings();

$warnings_issued = "y";

}

while (($needed_space) > ($backupstore_space)) {

# If there isn't enough space on the # backupstore, then go to backupstore_maint() # for the user to delete a backup or backups # to free enough space.

&backupstore_maint(); } }

# If the warnings have been dealt with successfully # then put up the main menu and process the user's # requests. Once all the state variables are set above # this loop will only run this menu.

&main_menu(%backups); }}

# Shouldn't ever get here, but if we do remember to unmount things ...

# Figure out what disks we have to work with using parted and hdparm # or udadmin (for the disk serial number). If we can't find a serial # number set it to unknown which will block user restores to this # disk (because we can't uniquely identify it). Populate global # associative array %disk_parted indexed by device/partition # (i.e. /dev/sda1) with partition start, partition end, partition size # partition filesystem type, partition flags (i.e. boot) and the # disk model number serial number (which may contain blanks which will # be stripped before storage) all seperated by the ":". The subroutine # will return 0, (with the data in %disk_parted) for success, or exit # with an error message via &unrecoverable_error on error. # This is a simple state machine parser, variable $state is # updated as each line is parsed. It may need changes for # internationalization if the format of parted changes by language it # currently assumes english! In such a case just replace the current # text being searched for with a variable in backup.base and add the # current text to backupmsgs.us file and the appropriate text for # your language to your language file. Same applies to df or any of # the other system tools if output changes by language.

# Cycle through all the partitions found by get_disks mounting the # fat and ntfs ones and looking for the various files (system, boot, # backup and backupstore) that we set and for the Windows directory # files to identify the system disk (in case the backup file is # misplaced by the user). Count the number of files found to complain # if there are errors later, and use df to obtain the amount of # space available and in use and the boot flags to identify what we # should be doing later.

# Since the tools don't (so far AFAIK) deal with msftdata # partitions, use dd to back up the entire partition and thus # set the size to the whole thing rather than the amount used. # Also set the backup file found flag (even though it wasn't!).

if (($ptype eq "") && ($flags =~ /msftres/)) {

# Fake a data entry for this unmountable file system. # Convert the size from MB/GB to 1K blocks to match # df.

# parse the data returned by df (skip the title line # which comes first!)

($title, $data) = split ('\n', $data, 2);

($dev, $total, $used, $avail, $percent, $mntpnt) = split(' ',$data);

if ($dev ne $drive_part) {

# Returned dev doesn't match the partition we # think we mounted so toss an error!

$err_msg = "$prog: Error: $DF -P $drive_part wrong drive $data\n";

&unrecoverable_error($err_msg); }

# Extract the drive from the drive/partition.

$drive = $drive_part;

chop $drive;

if (! defined($backupstores{"$drive"})) {

# If this is the first time we have seen this # drive, mark it as not a backupstore.

$backupstores{"$drive"} = "n"; }

# Check if the drive is marked as the Windows system # partition

if (-r "/mnt/backup/$WINDOWS_FILE") {

# if we found the Windows system disk file # mark that for later and count it.

$system = "y";

$system_tag_files_found += 1;

} else {

# Otherwise this isn't the Windows partition.

$system = "n"; }

if (-r "/mnt/backup/$BACKUP_FILE") {

# if we found the backme up file mark that in # the data for this partition.

$backup = "y";

} else {

# Otherwise this isn't (yet) a backup partition.

$backup = "n"; }

if (-r "/mnt/backup/$BOOT_FILE") {

# This partition is marked as a boot # partition for windows so mark it as such # and count it.

$boot = "y";

$boot_tag_files_found += 1;

} else {

$boot = "n"; }

if (-r "/mnt/backup/$RESERVED_FILE") {

# This partition is marked as a reserved # partition for windows so mark it as such.

$reserved = "y";

} else {

$reserved = "n"; }

if (-r "/mnt/backup/$BACKUPSTORE_FILE") {

# This is marked as a backupstore (valid or # invalid) mark the disk its on as a backupstore # which isn't eligable to have any partitions # backed up and count the tag file. Note # associative array %backupstores is global # as its needed later in process_disk_info.

$backupstore_tag_files_found += 1;

$backupstores{"$drive"} = "y";

if ($ptype eq "ntfs") {

# The backstore must be ntfs to support # greater than 2 gig files, if it isn't # then mark an error as this can't be # a backstore. As it is ntfs make this # the backstore. Set up the backupstore # files so we can mount it first thing # and store the log file we have been # accumulating in memory. Note that the # backupstore with the most free space # (if there is more than one) will be # used.

$backupstore = "y";

if ($backupstore_drive_part eq "") {

# This is the first so mark it.

$backupstore_drive_part = $drive_part; $backupstore_space = $avail;

$backupstore_percent = $percent;

$prelog_msgs .= "$prog: set $drive_part ($name) as backupstore\n";

} elsif ($avail > $backupstore_space) {

# The current candidate has # more free space than the # current choice so replace it.

$backupstore_drive_part = $drive_part; $backupstore_space = $avail;

$backupstore_percent = $percent;

$prelog_msgs .= "$prog: Warning: set $drive_part ($name) as new backupstore\n";

$warn = "y"; }

} else {

# This isn't an ntfs filesystem and # thus can't be a backstore so mark # it as a nonfatal error. If there # isn't another valid backupstore that # will be a fatal error.

# Last, check that /mnt/backup is not currently mounted and # unmount it if it is.

if (&is_mounted("/mnt/backup")) {

# Set up to log the partition information that we dismounted. # on the assumption it will succeed.

$prelog_msgs .= "$prog: Error at exit, unmountng /mnt/backup\n";

# This won't return if the unmount isn't successful!

&unmount("/mnt/backup"); }

# Successful finish so return %disks_df!

return (%disks_df);}

sub no_backupstore {

# No backupstore was detected. In case the backupstore is a USB # connected device, prompt the user to check it is connected and try # unplugging it and plugging it back in again.

local ($prog, $msg, $status, $res, $now);

$prog = "no_backupstore";

$msg = "\"\t Error: No backupstore device was detected.\n\nWe can not proceed without a backupstore to store backups on.\nIf your backupstore is USB connected, check that it is powered on\nand the USB cable is connected. If that appears to be so, try disconnecting and then reconnecting the USB cable to the computer.\nWait a minute or so after doing that for the drive to be recognized\nand then press < yes > (or just press enter) to try again.\n\nIf you select < no > below, a screen with shutdown options will come up\n (or you can just power the machine down from here).\n\"";

# Send the error message to the user and see what they want to do.

($status, $res) = &output_dialog_screen ("", "--yesno", $msg, "");

if ($status == 1) {

# User pressed cancel so go shutdown.

&shutdown();

}

$now = `$DATE`;

chop $now;

# User opted to try again so return to let them

$prelog_msgs .= "$prog: $now user elected to retry, returning\n";

return(0);}

sub process_disk_info {

# At this point we have identified and mounted a backupstore device # (or we wouldn't have gotten this far!). We have also flagged all # disks flagged as backupstores in array %backupstores (as no partition # on any of those disks are eligable for backup!) and scanned all the # drives and partitions for tag files, partition table types and boot # flage. So we cycle through all the partitions looking for a single # Windows system disk (preferably with both a tag file and containing # the Windows directories). If the tag file partition doesn't contain # the Windows directories flag a warning (as the tag file may be in # the wrong partition) but proceed with backup. If no system tag file # was found but AUTO_DETECT is enabled and Windows directories were # found set that as the system partition (if it isn't on a backupstore # drive!) and write a tag file, otherwise declare an error because we # don't have a system directory to backup. If we found a system # partition shedule it and the partition table of its disk for backup. # If the Windows directories are found on a partition other than the # system partition, issue a warning message. If more than one system # tag file is found issue a fatal error message. # Now that we have identified the system partition we rescan all # the partitions with the aim of scheduling any partition flagged for # backup to be backed up and checking that there is a bootable partition # on the same drive as the system partition (assuming the system # partition itself isn't bootable), declaring a warning if no bootable # partition is found.

# If AUTO_DETECT is set and we have a single marked system partition, # check for, tag and backup the boot partition and any reserved # partitions on the system drive (as Windows won't show any of those # partitions making it difficult for a Windows only user to tag them).

if (($AUTO_DETECT == 1) && ($sys_drive_part ne "")) {

foreach $drive_part (sort keys (%disks_df)) {

# Get the current drive

$drive = $drive_part;

chop $drive;

if ($drive eq $sys_drive) {

# This is the system drive so get the partition information # for this partition.

# Delete the pagefile if that option is enabled and recalculate # the space needed for the system partition without the page file # before calculating how much backup space is needed.

if ($TRUNCATE_PAGEFILE) {

# This is the system partition and TRUNCATE_PAGEFILE is set so # Mount the windows volume and truncate the pagefile to 0 # length to save some backup space. Windows will recreate the # page file at full length on the next boot and we save backing # up a potentially large, useless file.

%backups = &truncate_pagefile(%backups);

}

# Now set the system partition table type for restore in a global # variable.

# We have identified all the partitions to be backed up, so now # calculate how much space on the backstore we need for all the # partitions to be backed up. As a side effect log the partitions # scheduled for backup.

print LOG "$prog: Partitions scheduled for backup\n";

foreach $drive_part (keys %backups) {

($type, $fstype, $used, $name) = split (/:/,$backups{$drive_part});

$needed_space += $used;

print LOG "$prog: $type $fstype $drive_part $used $name\n"; }

$needed_space += $OVERHEAD;

print LOG "$prog: backupstore space needed $needed_space blocks\n";

return (%backups);}

sub mount_backupstore {

local ($prog, $res, $err_msg);

# Mount the backupstore on /mnt/backup to access the image files (and # potentially a configuration file to modify this script although # that isn't currently implemented). Use ntfs3g to mount because this # can't be a fat file system as there is a 2 gig file limit in fat. # As well use option "windows_names" to only allow filenames acceptable # to Windows (i.e. not including any of " * / : < > ? \ | )

$prog = "mount_backupstore";

# Use ntfs-3g rather than mount to make sure this is an ntfs file # system, use -o windows_names to only allow Windows legal characters # (not posix!) in file names.

# Mount the windows partition on /mnt/windows and then zero out the # file pagefile.sys. After doing that update the required size of the # partitions to be backed up (it will be considerably smaller after # this!).

# We won't get here if the above unmount fails, so mark /mnt/windows # as unmounted again and return.

$mnt_windows_mounted = "n";

return (%backups);}

sub open_log {

# So create a directory name that encodes application, date and time. # Thus a ls listing of the backup volume sorted in reverse order will # give you a list of directories that may contain backup files from # newest to oldest (as you are most likely to want the newest) to # select from during a restore operation. Each directory will contain # log files, image files, mbr/vbr files and md5 files of image and mbr # files from a backup / restore operation as well as the log file and # /var/log/messages file from that time period for your later enjoyment.

# If the description option is enabled, prompt the user for a # description of the backup.

if ($DESCRIPTION eq "y") {

# Get a text description of the backup from the user to include # in the file names.

$description = "";

$description_valid = "n";

while ($description_valid eq "n") {

$msg = "\'\t Enter a description of the backup:\'";

($status, $res) = &output_dialog_screen ("", "--inputbox", $msg, "");

if ($status == 0) {

# get and process the description (if any was entered). # Replace " ", "/", "\", "'", "`", ":", "<", ">", # "?", "*", "$", "|", ",", "#", "\n", "\r", ";", and '"' # with "~" to create a legal file name (and avoid # parsing problems when the filename is used in code # later!). The comma and "#" are used as field # separators and thats why they are being translated. # The ";" causes a shell error from dialog. As well a # trailing "." at the end of the name is illegal in Windows, # so translate them to a "~" if present.

# Since a trailing "." is also illegal, translate # "."s at the end of the name to "~" to avoid an error # on directory creation.

$description =~ s/\.+$/\~/g;

} else {

# If no illegal charaters set the input as the # description.

$description_valid = "y";

}

if ($description_valid ne "y") {

# Print the substituted string and ask the user # if it is OK. If not go back to get new input.

$msg = "\"\n\n\t Error: invalid characters in the input\n\nCharacters invalid in a file name were found in the description and translated to:\n\n$description\n\n If this name is acceptable click < yes > (or press enter)\n\n otherwise click < no > to enter a new string.\n\"";

$msg = "\"The name\n\n$dir_name\n\nis already in use on the backupstore. While this shouldn't have occurred since the name is partly based on time, waiting for a minute should fix the problem by creating a new name.\n\nIf you would like to try that click \< Yes \> or press enter.\n\n (after waiting for a minute) To return and try again.\n\nIf you click \< No \> (or use tab to move to it and press enter)\n\n The exit options will come up.\n\"";

# Do a precreate df to see that the backupstore is # in fact mounted as it should be.

$err_msg = `$DF`;

$prelog_msgs .= "$prog: pre mkdir df: $err_msg\n"; }

$res = `$MKDIR /mnt/backup/$dir_name 2>&1`;

if ($? != 0) {

# Still no log file so this is all you will get. Disk # full is a likely candidate here. It is also likely # unrecoverable. Note mkdir puts `s in its error # message so the response must be edited to keep # dialog happy.

# Then tell the user there is a problem and suggest # possible solutions. Then exit as this is fatal.

$msg = "\'Error:\n\n No system disk detected, backups or restore is not possible\n\nIf Windows will boot, check that the file:\n\nC:\\$WINDOWS_FILE\n\nexists. If it does not, use notepad to create that file and try again.\n\nOtherwise you will need to seek support.\n\nClick \< Yes \> or press enter to get the exit options to reboot to\n\nWindows or shutdown.\n\'";

# Then tell the user there is a problem and suggest # possible solutions.

$msg = "\'Error:\n\n More than one $WINDOWS_FILE file found.\n\nIf Windows will boot, check that the file $WINDOWS_FILE exists only on the C: drive.\nIf it exists on other drive(s) then replace them with the $BACKUP_FILE file and try again\nIf you can't find a second $WINDOWS_FILE you will need to seek support\n\nClick \< Yes \> or press enter to get the exit options.\n\'";

} elsif (($sys_drive_part eq "") && ($system_tag_files_found == 1)) {

# System partition is on a tagged backupstore drive. # Log the problem.

# Then tell the user there is a problem and suggest # possible solutions.

$msg = "\'Error: The Windows partition (drive C:) has both a $WINDOWS_FILE file and a $BACKUPSTORE_FILE file on it.\n\nIf Windows will boot, you need to move the $BACKUPSTORE_FILE file to a different disk and try again.\nIf you can't do that you will need to seek support\n\nClick OK or press enter to get the exit options and reboot to Windows\n\'";

# Then tell the user there is a problem and suggest # possible solutions.

$msg = "\'Error: No boot tag file found.\n\nYou will likely need to get your support person to set the correct flag on the boot partition\n\nClick OK or press enter to get the exit options and reboot to Windows\n\'";

# Then tell the user there is a problem and suggest # possible solutions.

$msg = "\"\n\n\t Error: Too many boot tag files found.\n\n You will likely need to get your support person to set the\n\n $BOOT_FILE\n\n file on only the correct boot partition\n\n Click < Yes > (or press enter) to get the exit options.\n\n (or just power off right from here as we have shutdown already.)\n\"";

}

# Close any open files in case the user just powers off.

&close_files();

# Display the error message (which is fatal)

($status, $res) = &output_dialog_screen ("", "--yesno", $msg, "");

# and then exit.

&shutdown(); }

# If we have logged warnings during earlier processing, advise the # user to bring them to the attention of their support person before # proceeding.

if ($warn eq "y") {

print LOG "$prog: Warning messages being brought to the attention of the user\n";

# Format a dialog message for the user and wait for # confirmation.

$msg = "\"\n\n\t Warning message(s) have been written to the log file.\n\n\n\n\t Please alert your support person to check the log file.\n\n (to make sure the operation was successfull before it is needed!)\n\n\n\n\t Click \< Yes \> or press enter to proceed.\n\"";

# User pressed cancel or esc, so fall through to go back to # the menu (or a dialog error occurred but we can't tell!)

} elsif ($res == 1) {

# Display a help screen (and incidentally recover gracefully # from forgetting to select an action which I usually do ...) # Note the use of --no-collapse to control dialog formatting.

$msg = "\' Welcome to SimpleBackup\n\n\t On the previous screen you can select:\n\nBackup - to back up the data on your hard disk.\n\nExit options - exit when finished by rebooting or shutting down.\n\nRestore - to restore the hard drive from a previous backup.\n\nRemove backups - to delete a backup to free space on the backupstore.\n\nShell - to get a command prompt (for your support person).\n\n\nNow any of \"enter\" < Yes > or < No> will get you back to the main screen\'";

$msg = "\"\t An unrecoverable error has occurred\n\n Please write this message down and give it to your support person:\n (or leave the machine here for your support person if possible)\n\n$err_msg\n\n\n Then press enter or < Yes > and the shutdown menu will come up.\n\n\t (or just power down now as we are shutdown already.)\n\"";

} else {

# Send the error message to the log file.

print LOG "$prog: $err_msg";

# And a generic message to the user (without the details).

$msg = "\"\n\n\t An unrecoverable error has occurred and been logged.\n\n You will need to ask your support person to check the log\n\n and correct the error.\n\n\n\n\n\n Then press enter or click < Yes > and the shutdown menu will come up.\n\n (or just power down now as we are shutdown already.)\n\"";

# since this will fork to do the shutdown, exit perl to # prevent reopening the files and restarting the menu before # the shutdown takes place.

exit(0);

} elsif ($res == 2) {

# Reboot if requested

`$SHUTDOWN -r now`;

# since this will fork to do the shutdown, exit perl to # prevent reopening the files and restarting the menu before # the reboot takes place.

exit(0);

} else {

# For all other responses, drop to a Linux shell (this won't # return!)

&shell(); }}

# Subroutine to back up the Windows system, the MBR/VBR, the Windows partition# and a separate boot partition if there is one. Also called by restore to do# a backup before the restore (enabled by default but can be disabled, although# that is a bad idea ...).

# Create a new local timestamp so each backup is unique in case the # same backup gets run more than once for some reason. All the files # in a backup will share the same timestamp.

$local_timestamp = `$DATE +%Y-%m-%d-%H-%M-%S`;

chop $local_timestamp;

# Cycle through the partitions selected for backup, backing them up # as we go.

foreach $backup_part (sort keys (%backups)) {

# Cycle through the partitions to back up, create an appropriate # file name and then back them up.

($type, $fstype, $used, $name) = split(/:/,$backups{$backup_part});

# Create a filename from the backup_type: SB for a user # requested backup, SR for a backup done before a restore # operation, filesystem type (to select the appropriate restore # program either manually or automatically later), partition # number on the drive (this will be MBR for the mbr(s)), # timestamp, and disk serial number (or unknown if we don't # have a serial number). Thus the file name is a simple # database entry that describes what the file is and what # drive (and thus partition table, to determine the partition # size if necessary) it came from.

# Subroutine to restore a partition. For safety we will first do a # backup of the partition to be restored (so you can recover it if # this was a mistake!). The user will then be given a choice of the # backup files for this disk stored on the backupstore to restore from.

# Didn't find the system disk (shouldn't occur, but just in # case ...)

$err_msg = "$prog: Error didn't find system disk $sys_drive_part\n";

&unrecoverable_error($err_msg); }

if ($name =~ /unknown/) {

# If we didn't find a serial number on the disk then backup # isn't simple and the restore needs to be done by the # support person not ths script. So give the user the bad # news.

$msg = "\"\n\n\n\n\n\t Unfortunatly this disk does not have a serial number.\n\n\t Thus this program can not safely restore it.\n\n\t You will need to get your support person to do the restore manually.\n\"";

($status, $res) = &output_dialog_screen ("", "--yesno", $msg, "");

return (0);

}

# Get the backup dirctory names for the disk serial number identified # above from the backupstore and sort them in reverse time order so # the latest is first on the assumption that the user will want the # newest backup to work on.

($filenames, @res_disp) = &list_restore_imgs($name);

if ($#res_disp == -1) {

# There are no files to restore, so give the user the bad news.

$msg = "\"\n\n\n\n\t Error: no suitable backup image for this disk could be\n\n\t found on this backupstore. If you have another backupstore\n\n\t try connecting that in place of the current one and try again.\n\n\t Otherwise contact your support person for assistance\n\"";

($status, $res) = &output_dialog_screen ("", "--yesno", $msg, "");

# Then return to the main menu.

return (0); }

# Then present the list to the user and ask them to select the file # to restore. Set $sel to "on" so the first entry will be preselected

# Set the backup file to restore from and check that we have all the # required files to do the restore so if there is an error we won't # waste time doing the pre restore backup (if selected). # Split out the timestamp from the selected restore as that will be # used to select the needed files from $filenames.

# Cycle through the filenanes returned and select only those with a # matching backup type and timestamp and store them for restore by # file type. Save any .md5 files found in an associative array indexed # by file name to make checking easier.

# Now do some error checking, first make sure there is only 1 system # partition and one partition table present.

if (($system_found != 1) || ($part_table_found != 1)) {

# We need 1 system partition and 1 partition table, anything # else is a fatal error. This could still fail if someone # deleted the boot partition, but it can also be part of the # system partition so one not being present isn't necessarily # an error.

# Then compare it to the one in the restore file. Note this # will need work if we decide to restore data partitions from # other than the system disk (as there will be multiple part # tables and we would have to find the correct one here).

# The partition tables are different, so ask the user if that # is OK since data loss may occur if we replace the partition # table.

$msg = "\"\n\t Important warning:\tThe partition tables are different!\n\n\t If you are changing from one Windows version to another\n\t (such as Win7 to Win10) this may be normal.\n\n\t However if you proceed with the restore it is possible\n\t that important data will be lost, so be very sure this is OK.\n\n\t The safest course of action is to select the default \< Yes \> answer to cancel the restore and then check with your support person to make sure this is expected. If you are sure it is OK, select \< No \> below to not cancel the restore.\n\n\tDo you want to cancel the restore as data loss may occur?\n\"";

print LOG "$prog: Warning: User elected to replace the partition table and proceed with the restore which may cause data loss.\n";

}

if ($DO_MD5) {

# OK you asked for it :-). Cycle through all the files (other # than the mbr which we did above) to be restored and compare # their current md5 check sum with the one on file from the # backup. This will take a long time.

# Note we haven't reported a missing md5 yet (so we will only # do so once per restore.)

# Things look ok to do a restore, so now do the safety backup if that # is enabled.

if ($BACKUP_BEFORE_RESTORE) {

print LOG "$prog: starting backup of current disk\n";

# Backup the current volume first set the backup type to "SR" # so we know it was a pre restore backup rather than a user # requested backup during later restores (as this probably # isn't a good choice to restore from normally!) and for # backupstore maintance operations. Backup won't return if # there is an error.

$res = &backup ("SR", %backups); }

# If we get here we are now ready to do the actual restore having # backed up the current disk.

if ($part_tables_different eq "y") {

# The partition tables are different so restore the old # partiiton table first (as that is required before we can # restore the previous partitions!)

# List the backup images on the backupstore that could be deleted to # free up space on the backupstore. Present them oldest first on the # assumption that older backups are less important. The SR (restore # safety) backups are presented last as they may be the more valuable # ones.

$msg = "\"\t The Backupstore is $backupstore_percent full\n\n However none of our image files were found to delete.\n\n You can either reboot in to Windows and move or delete files from\n the current backupstore drive, replace the current backupstore\n with another, or get your support person to free up space on the\n current backupstore.\n\n\n There isn't anything further that this program can do.\n\n\n Press Enter or click \< Yes \> to bring up exit options\n\"";

($status, $res) = &output_dialog_screen ("", "--yesno", $msg, "");

&shutdown();

} else {

# Then present the list to the user and ask them to select the # file to delete.

# The user pressed cancel so abort and go back to the # initial selection screen!

return ($status); }

# Save the index value that the user chose.

$del_index = $res;

# Get the selected name.

$backup_name = $del_disp[$del_index];

# Confirm the user really wants to delete this backup! # Note the user must choose the "no" response to actually # do the delete, just pressing enter will cancel the deletion.

$msg = "\"\n You have requested backup:\n\n $backup_name\n\n Be deleted.\n\n You must select \'no\' below to complete the deletion.\n\n The default answer of \'yes\' will cancel the deletion\n leaving the backupstore unchanged!\n\n\n Do you want to cancel this deletion?\n\"";

($status, $res) = &output_dialog_screen ("", "--yesno", $msg, "");

if ($status == 0) {

# The user chose yes, so cancel this deletion and # return to the main menu.

$msg = "\"\n\n\n Deletion cancelled as requested.\n\n\n\n\n\n\n\n\n\n\n Press enter or click < Yes > to return to the main menu,\n\"";

# Tell the user what happened and wait til they # acknowledge before returning.

($status, $res) = &output_dialog_screen ("", "--yesno", $msg, ""); }

# User chose no so delete the backup image files. # Split out the selected file information to match.

# Then select the files that match from $filenames and delete # them one at a time til there aren't any more matches. The # combination of $backup_type and $backup_timestamp will insure # all the image files from the selected backup are deleted. # Note the log files (which are small) are left for the # support person so they know what went on and should be # manually deleted later. Now get the first filename:

# Now go through the directories one at a time and select the files # representing system partition backups as there will be only one of # them per backup. Put the file names followed by the directory they # are in, in to an array sorted in time order newest first. Thus we # can easily select the file name based on the index value returned # by dialog. First put the "SB" files (user requested backups) # in time order followed by the "SR" files (restore safety backup) # again in time order as being the less likely restore target. There # is an unlikely but possible case where a directory may contain more # than one backup (they would likely be identical, as there won't be # an exit from the script between them to cause changes, but it is # possible so lets allow for it). In that case the backup file # timestamps will be different but the directory will be the same.

# Split out the first file name.

($dir_file, $files) = split(/\n/,$files,2);

while ($dir_file) {

# If this is a log file or syslog file, it won't have the # "#" in the name (and -w objects to the undefined value # in $filename) so just skip it if there isn't a #. Because # we searched on a disk serial number we shouldn't hit any # log files but just in case. As well skip the md5 files so # that there are only image files present.

if ($dir_file =~ /#/) {

# then split the directory name and the file name

($dir, $filename) = split (/#/,$dir_file,2);

# Save a copy of the fully qualified file name to # return to the caller in $filenames delimited by # a newline.

# Save only the system files (of which there will be # only one per backup) so each backup will only appear # in the list once. We need to ignore the md5 files # however as they will provide a dup file name.

# put the SB files in to the array in reverse # order (because ls sorted them in descending # time order and we want ascending time order) # with the type, filename (not directory!) # timestamp, and description (to display to the # user for selection to restore).

# Do the same as above to the SR (recovery # backup) files in to a temp array. After this # we will append these files to the end of # the SB file array above on the assumption # the safety backup files will be the least # likely to be restored.

# Now go through the directories one at a time and select the files # representing system partition backups as there will be only one of # them per backup. While doing this maintain a count of backups for # the disk in question that have been found (we will favor deleting # the oldest of the most numerous backup as being least valuable first!) # in an associative array.

($dir_file, $files) = split(/\n/,$files,2);

while ($dir_file) {

# If this is a log file or syslog file, it won't have the # "#" in the name (and -w objects to the undefined value # in $filename) so just skip it if there isn't a #. As well # skip the .md5 files to leave only image files (of which # there should only be one).

if ($dir_file =~ /#/) {

($dir, $filename) = split (/#/,$dir_file);

# Get just the description (if there is one) from the # directory.

($sb, $dir_timestamp, $desc) = split(/,/,$dir,3);

# Remove the trailing "/" from the description

chop ($desc);

# Then get the date and time of the actual backup file # and the disk name.

# Then save a copy of the fully qualified file name # which will be one of the image files (not the log # file or syslog files both of which are small and # will be ignored in this) for later potential # deletion. These will be selected based on the # ${backup_timestamp} field in the desc associative # arrays above.

$filenames .= "$dir_file\n";

}

($dir_file, $files) = split(/\n/,$files,2); }

# We now have a picture of all the image files on the backupstore # and how many generations of backup are available for any particular # disk. So arrange that information in to a list to present to the # user to select backups to delete. Favor (by listing first) the oldest # of the backup with the most generations (as being least valuable). # First set up to index the associative arrays by count rather than # disk name and fill it with newline delimited descriptions to create # a select array for the user.

foreach $name (keys %sb_count) {

$sb_by_count{"$sb_count{$name}"} .= "$sb_desc{$name}";

}

# Then do the same for the SR backups.

foreach $name (keys %sr_count) {

$sr_by_count{"$sr_count{$name}"} .= "$sr_desc{$name}";

}

foreach $count (sort numerically (keys %sb_by_count)) {

# Now form a display array with the largest number of backup # generations and oldest times first.

$entries = $sb_by_count{$count};

# Since a given count may have more than one entry we need # to split them out by the \n that delimits them.

($line, $entries) = split(/\n/,$entries,2);

while ($line) {

# Then insert the entry in to the array to display # to the user.

push (@del_disp, $line);

($line, $entries) = split(/\n/,$entries,2);

} }

# Then do the same for the SR entries.

foreach $count (sort numerically (keys %sr_by_count)) {

# Now form a display array with the largest number of backup # generations and oldest times first.

$entries = $sr_by_count{$count};

# Since a given count may have more than one entry we need # to split them out by the \n that delimits them.

($line, $entries) = split(/\n/,$entries,2);

while ($line) {

# Then insert the entry in to the array to display # to the user (after all the SB entries).

push (@del_disp, $line);

($line, $entries) = split(/\n/,$entries,2); } }

# Now return the complete set of filenames and the selection array # to the caller.

return ($filenames, @del_disp)}

sub update_backupstore_space {

# Update the space available on the backupstore so the space tally is # current. Make everything except $backupstore_space and # $backupstore_percent (which are global) local to avoid naming # conflicts.

# Normal exit so copy the /var/log/messages file to the backupstore # then unmount the file systems in preparation for departing, then # close the log file and return to the caller to let them do what ever # action (shutdown, reboot, exit to shell) was requested with all # the files closed (so a power down won't hurt). Because this may have # been called from &unrecoverable_error do all error checking here # without calling any other routine to avoid a potential recursive # error loop (specifically don't call &is_mounted()).

local ($prog, $data);

$prog = "close_files";

if ($DEBUG) {

if ($log_open eq "y") {

print LOG "debug: entered $prog\n";

} else {

$prelog_msgs .= "debug: entered $prog\n"; } }

# open /var/log/messages and seek to the start point determined when # this script started. Then copy from there to end of file to the # log drive so we have the system log messages on the backup drive. # However check if the backupstore is mounted first!

# first file with no md5, so ask the user what to do # as this may be a backup from before md5s were enabled.

$msg = "\"\n\n There isn't an MD5 file from the backup.\n\n This isn't necessarily a problem, as MD5 checking may have been off when the original backup occurred. While we can't verify that the file to restore is OK, we can still do the restore if you wish. You should bring this to the attention of your support person if you haven't already.\n\n Press enter or click < Yes > to cancel the restore.\n\n Click or select < No >\n\n to proceed with the restore without checking the backup files\n\"";

($status, $res) = &output_dialog_screen ("", "--yesno", $msg, "");

if ($status == 0) {

# User requested the restore be cancelled so # return 1.

$err_msg = "$prog: Error $filename has no md5 in the backup, aborting at user request\n";

return (1, $no_md5_reported); } }

# User has elected to proceed. So log the failure and proceed # after noting we have asked already so we don't repeat the # warning.

$no_md5_reported = "y";

print LOG "$prog: Warning: filename $filename has no md5 in the backup ignoring at user request\n";

# The user has elected to proceed without checking the MD5s # so return success even though we haven't checked the MD5.

return (0, $no_md5_reported); }

$msg = "\"\n\n Checking the MD5 of image file:\n\n$filename\n\n This may take some time\n\"";

# the operation (backup or restore) has ended successfully so first # close all the files.

&close_files();

# then proceed according to the $prompt setting

if ($prompt eq "shutdown") {

# Then shut down

`$SHUTDOWN -P now`;

# since this forks, now exit perl so we don't reopen the files

exit (0);

} elsif ($prompt eq "reboot") {

# then reboot

`$SHUTDOWN -r now`;

# since this forks, now exit perl so we don't reopen the files

exit (0);

} else {

# then put up a dialog box til the user acknowledges that # operation completed OK (or as noted just powers off).

$msg = "\"\n\n The requested $operation operation completed successfully\n\n\n Remove the CD or USB key and either\n\n\n\n Press enter or click \< Yes \>\n\n to return to the exit options if you want to reboot or\n\n Just power down if you are finished.\"";

($status, $res) = &output_dialog_screen ("", "--yesno", $msg, "");

# Remount the backupstore on /mnt/backup to access the image # files as the user has chosen to do more.

&mount_backupstore();

# Reopen the log file but log a message with the current time # as well.

# Unfortunatly the cancel key counts as an error along with real # errors, so we just return the status and assume there isn't a # real error (which absent a coding error, there shouldn't be).

return ($status, $res);}

sub commas {

local ($_) = @_;

1 while s/(.*\d)(\d\d\d)/$1,$2/; $_;}

sub numerically {$b <=> $a;}

sub interrupt_handler {

local ($sig) = @_;

# First log that we caught a signal.

if ($log_open eq "y") {

print LOG "Caught a SIG$sig shutting down\n";

} else {

$prelog_msgs .= "Caught a SIG$sig shutting down\n";

}

# Then close any open files and dismount the file systems if possible.

&close_files();

# then put up a dialog box til the user acknowledges that something # bad has happened (and what to do to restart) before exiting.

$msg = "\"\n An unexpected interrupt has occurred.\n (perhaps pressing the cntrl-C key?)\n\n If you don't think that you pressed the cntrl-c key, please bring this to the attention of your support person to see what the error was.\n\n Any operation in progress has been terminated and the files closed,\n and should be considered to have failed.\n\n If this wasn't intentional and you wish to continue, press enter or click \< Yes \> and then type autorun at the command prompt to restart the backup. You can also just reboot or power cycle the machine as the files have been closed ready for shutdown.\n\"";

Well its better than it first appeared, while the post software seems to have stripped the formatting if you copy and past the 5 posts in to a single file perl doesn't seem to care about the formatting and it appears on a quick test to still run OK. Either a pretty printer or a manual format should restore the formattingto human readable though.