home *** CD-ROM | disk | FTP | other *** search
- /*********************************************************************
- GHBackup.rexx - Glenn Holliday's Automated Backup
-
- AREXX program to run MRBackup for incremental backups.
-
- Calling template:
-
- rx GHBackup [homePath [backupPath [lastFullBackupDate]]]
-
- Description:
-
- This program can easily be adapted to your own set of backup
- rules. You _do_ want to examine the code and tweak it, so it
- runs on the schedule you want to use for your personal backup
- policy. Note in particular that most people will want to replace
- my own scheme for daily backups. You'll likely either ignore dailies
- (you wouldn't do that, would you? :-) or set the parameters to format
- a new disk just like I have it doing for a weekly backup.
-
- (Note: I've tagged some areas that are candidates for customization
- with *** CUSTOMIZE *** - MRR.)
-
- This software is completely public domain.
-
- Author:
- --
- Glenn | ...!uunet!anagld!dahlgren!glenn (UUCP)
- Holliday | glenn@dahlgren.sed.csc.com (fully domained)
- October 1 1994
-
- Since it's small, the complete software engineering document
- set follows. I thought this might be interesting, it's the easiest
- way to explain what backup policies are and how I approached them,
- and it may answer questions of the form "why in the world is _that_
- in the code?" Feel free to strip out this part if you wish:
-
- Problem analysis:
-
- Implement a set of rules to do incremental backups.
- Recognize daily, weekly, and monthly backups as different cases.
- Automatically run MRBackup and set up its parameters for each case.
-
- Backup rules:
-
- Keep a full backup set, a monthly backup set, a weekly
- backup set, and six daily backup sets (one for each day between
- weekly backups). Backup every day.
-
- Assume that full backups (the entire disk) are done by hand
- when needed. On the first day of a month, backup all files changed
- since the date of the most recent full backup. On the first day of
- a week (Sunday), backup all files changed since the date of the most
- recent monthly backup. On any other day, backup all files whose
- archive bits are clear (these are all files changed since the last
- backup). On every backup, set the archive bits of all files backed
- up.
-
- Monthly and weekly backups are done to a set of diskettes.
- Daily backups (assuming a small amount of change each day) are done to
- subdirectories of a single diskette, named for each day.
-
- This program assumes that you keep a record of the date
- on which the most recent full backup was done. It does not provide
- for making a full backup, or for storing that date. Some interesting
- extensions could help out with that chore. I do full backups whenever
- my set of monthly backup floppies overflows a box full, store the date
- in a file in s:, and have my "backupifold" script supply that date
- when it calls GHBackup.rexx.
-
- Design to automate the backup rules:
-
- Run mrbackup.rexx daily. In this program,
- Get the current date, day of week,
- date of last monthly backup, and
- date of last full backup.
- Classify current date as new month, new week, or ordinary day.
- (This partitions the set of all dates into equivalence
- classes, where each equivalence class is identified by
- one of the values of a data type with three possible values.)
- If the current date is:
- new month,
- new week,
- ordinary day
- Run MRBackup with the selected parameters.
-
- Implementation:
-
- GHBackup.rexx is run daily by some other mechanism. If you
- leave your system on all the time, you probably want to put an entry
- in your crontab to run it at the same time each day. I turn my
- system off overnight, so I have cron run a program called "backupifold"
- several times a day. It checks a timestamp of the last backup, and
- runs GHBackup.rexx if the timestamp is more than a day old. I did not
- include this program because it uses some shenaigans I don't like to
- recogize elapsed time. If I ever get around to writing the more
- elegant solution I have in mind, I'll give it away.
-
- Input arguments:
-
- If any of these are absent, GHBackup.rexx uses defaults. The
- defaults are declared as ARexx variables at the beginning of the
- code.
-
- arg description default
-
- homePath String, name of disk or other DH1:
- filepath to the disk area to be
- backed up
-
- backupPath String, filepath to the backup DF0:
- medium or directory
-
- lastFullBackupDate String, date in AmigaDOS format Jan 1 1978
- on which the last full backup
- was done.
-
- This program follows the design outline, using several
- functions for modularity. The outline of the code is as follows:
-
- date = date()
- classifydate(date)
- select
- newmonth
- setNewMonth()
- newweek
- setNewWeek()
- daily
- setNewDay()
- end of program
-
- classifydate(date)
- parse date to get day, month, year, day of week
- if day is 1, classify as newmonth
- else if day of week is Sunday, classify as newweek
- else classify as daily
- return classification
- setNewMonth():
- (Back up everything since last full backup)
- build "files since" date of last full backup,
- select df0:, AmigaDos, quick format, ignore archive bits,
- and no compression
- setNewWeek():
- (Back up everything since last monthly backup.
- Assume date of last monthly backup is the first of the month)
- build "files since" first day of current month,
- select df0:, AmigaDos, quick format, ignore archive bits,
- and no compression
- setNewDay():
- (Backup up everything since most recent backup.
- Assume that's everything since archive bits were last set,
- = everything with archive bits clear)
- build backup path, AmigaDos, no format, test archive bits,
- and 16 bit compression
- *********************************************************************/
-
- /********************
- body of ARexx program
- ********************/
-
- /*******************************************
- During debugging, want to see all the errors
- *******************************************/
- /* TRACE INTERMEDIATE */
- signal on ERROR
- signal on BREAK_C
-
- options results
-
- /*** CUSTOMIZE ***/
- defaultHomePath = "DH1:"
- defaultBackupPath = "DF0:"
- defaultLastFullBackup="01-Jan-78 00:00:00"
-
- ARG homepath backuppath lastFullBackup
-
- if homepath = '' then
- homepath = defaultHomePath
-
- if backuppath = '' then
- backuppath = defaultBackupPath
-
- if lastFullBackup = '' then
- lastFullBackup = defaultLastFullBackup
-
- /****************************************************************
- Arexx parsing has the nasty habit of leaving the leading space on
- the last of a string of arguments that it parses from a command
- line. There are code-intensive ways of checking and cleaning up
- all the arguments. Since I know it'll only be a problem on the
- last one, here's a cheap way of cleaning it up.
- ****************************************************************/
-
- lastFullBackup = strip(lastFullBackup)
-
- /***************************************
- Be sure MRBackup is running. Assume
- only one copy needed, always address the
- first MRBackup Rexx port.
- startMacro(port, program) is a standard
- Rexx macro that, if its first argument
- is not already there, runs its second
- argument and waits for its first
- argument to appear.
- ***************************************/
- toReturn = startServer('MRBackup_#1', 'mrbackup:mrbackup')
- address 'MRBackup_#1'
- takecontrol
- poptofront
-
- /***************************************
- Get date, including weekday.
- ***************************************/
- date = date('normal')
- weekday = date('weekday')
-
- /**********************************************
- returns dateclass = newmonth, newweek, or daily
- **********************************************/
- dateclass = classifydate(date, weekday)
-
- /***************************************
- send ARexx commands to set MRBackup
- parameters for the type of date
- ***************************************/
- select
- when dateclass = 'newmonth' then
- toReturn = setNewMonth(homepath, backuppath, lastFullBackup)
- when dateclass = 'newweek' then
- toReturn = setNewWeek(homepath, backuppath, date)
- when dateclass = 'daily' then
- toReturn = setNewDaily(homepath, backuppath, weekday)
- otherwise
- do
- Say 'unrecognized date type ' || dateclass
- toReturn = 'FAIL'
- error /* Use error: function to exit */
- end
- end
-
- /***************
- Start the backup
- ***************/
-
- if toReturn = 'OK' then
- do
- backup
- if rc ~= 0 then
- do
- say "Backup failed. Error code: " || rc
- error /* Exit through error handler */
- end
- quit /* command to MRBackup to quit */
- end
- else
- error
- exit 0 /* end of program, success return code */
-
- /*********************************************************************
- classifydate(date, weekday)
-
- Function to decide if a date is the first of month or week
- *********************************************************************/
-
- classifydate:
-
- date = arg(1)
- weekday = arg(2)
- parse var date day month year garbageintail
- select
- when day = 1 then
- datetype = 'newmonth'
- when weekday = 'Sunday' then
- datetype = 'newweek'
- otherwise
- datetype = 'daily'
- end
-
- return datetype
-
- /*********************************************************************
- setNewMonth(homepath, backuppath, lastFullBackup)
-
- Inputs:
- homepath String, filepath to be backed up
- backuppath String, filepath to be backed up
- lastFullBackup String, date in AmigaDOS format of
- last full backup
-
- Function to set MRBackup's parameter for monthly backup.
- Back up everything since last full backup. Build "files since" date
- of last full backup, select df0:, quick format, ignore archive bits,
- and no compression.
- *********************************************************************/
- setNewMonth:
-
- homepath = arg(1)
- backuppath = arg(2)
- lastBackup = arg(3)
-
- /**********************************************************
- The date to backup from is midnight of the date of the last
- full backup.
- **********************************************************/
-
- parse var lastBackup weekday datestring timestring
-
- /*** CUSTOMIZE ***/
- testdate = datestring || " 00:00:00"
- compression = 'None'
- formatting = 'Quick'
- testbits = 'No'
- comment = "Monthly backup: All files since date of full backup"
- toReturn = setParameters(homepath, backuppath, comment, compression, ,
- formatting, testdate, testbits)
-
- return toReturn
-
- /*********************************************************************
- setNewWeek(homepath, backuppath, date)
-
- Inputs:
- homepath String, filepath to be backed up
- backuppath String, filepath to be backed up
- date String, current date in ARexx form
- (as opposed to AmigaDOS form)
-
- Function to set MRBackup's parameter for weekly backup.
- Back up everything since last monthly backup, = first day of the
- month in the input date. Build "files since" date of the first of
- the month, select df0:, quick format, ignore archive bits,
- and no compression.
- *********************************************************************/
-
- setNewWeek:
-
- homepath = arg(1)
- backuppath = arg(2)
- date = arg(3)
-
- /*************************************************
- Get the month, set new date to first day of month.
- Translate 19yy to yy, use midnight on the first.
- *************************************************/
- parse var date oldday month year rest
- yearpart = right(year, 2)
- testdate = '01-' || month || '-' || yearpart || " 00:00:00"
-
- /*** CUSTOMIZE ***/
- compression = 'None'
- formatting = 'Quick'
- testbits = 'No'
- comment = "Weekly backup: All files since first of this month"
- toReturn = setParameters(homepath, backuppath, comment, compression, ,
- formatting, testdate, testbits)
-
- return toReturn
-
- /*********************************************************************
- setNewDaily(homepath, backuppath, weekday)
-
- Inputs:
- homepath String, filepath to be backed up
- backuppath String, filepath to be backed up
- weekday String, current day of the week
-
- Function to set MRBackup's parameters for daily backup.
- Back up everything that has archive bits clear, build a backup
- filename from the current weekday, no format, and 16-bit compression.
- *********************************************************************/
- setNewDaily:
-
- homepath = arg(1)
- backupdev = arg(2)
- weekday = arg(3)
-
- /*** CUSTOMIZE ***/
- backuppath = backupdev || weekday || "/"
- testdate = 'nil'
- compression = '16-Bit'
- formatting = 'None'
- testbits = 'Yes'
- comment = "Daily backup: All files with archive bit clear"
- toReturn = setParameters(homepath, backuppath, comment, compression, ,
- formatting, testdate, testbits)
-
- return toReturn
-
- /*********************************************************************
- setParameters(homepath, backuppath, comment, compression, formatting,
- testdate, testbits)
-
- Inputs:
- homepath String, filepath to disk area to back up
- backuppath String, filepath to disk to which the backup
- is to be saved
- comment String, to display in MRBackup's comment gadget.
- Informs user what type of backup is being done.
- compression String, None or 16-Bit.
- testdate String, an AmigaDOS date or nil. nil indicates
- the date is not tested.
- testbits String, YES or NO. If archive bits are tested,
- testdate is not used. If archive bits are not
- tested, testdate is used.
-
- Returns:
- toReturn String, OK or FAIL (conforms to the code returned
- by each MRBackup command). Tells the caller if
- everything was set successfully.
-
- This function sends ARexx commands to MRBackup to set the
- parameters calculated by earlier functions. It tests for successful
- completion of each ARexx message.
-
- Assume that standard parameters common to all types
- of backups (e.g. mode = AmigaDOS) are set in the usual
- MRBackup.init, which MRBackup loaded on startup.
- *********************************************************************/
-
- setParameters:
- arg homepath, backuppath, comment, compression, formatting, testdate, ,
- testbits
-
- /*************************************************
- Check the backup disk is present before giving the
- setbackpath command. If it's a device name,
- MRBackup checks it when it's time to begin writing
- to it. If it contains a directory path,
- MRBackup requires that directory be available on
- a mounted device when the setbackpath command
- is issued.
- *************************************************/
- if right(backuppath, 1) ~= ':' then
- do while ~exists(backuppath)
- string = "'Please mount " || backuppath || " in any drive'"
- 'getchoice' '"'string'"' 'OK'
- end
-
- setfilemode "Replace"
-
- /***************************
- This section sets all the
- MRBackup parameters. The
- strategy is:
-
- set a parameter
- as long as no error yet,
- keep going to set more
-
- Most of the ARexx commands
- put a return code, OK or
- FAIL, in the variable
- result. Test result after
- each command, and keep going
- as long as it's OK.
-
- Some of the commands put
- something else in result.
- For these, check the ARexx
- return variable rc. Set
- result to indicate the same
- state that rc indicates.
- This keeps the current
- state consistent in result,
- and lets it be used
- consistently to test if
- the next parameter should
- be tested.
-
- If any command returns
- a failure code, all of
- the remaining tests
- fall through, and no
- more commands are sent to
- MRBackup.
- ***************************/
-
- if result ~= 'FAIL' then
- setbackpath backuppath /* returns result = OK or FAIL */
- if result ~= 'FAIL' then
- sethomepath homepath /* returns result = current path, check rc */
- if rc ~= 0 then
- do
- result = 'FAIL'
- end
- else
- listing 'YES' /* returns result = OK or FAIL */
- if result ~= 'FAIL' then
- setarcbits 'yes' /* always set the bits after we finish backing up */
- if result ~= 'FAIL' then
- 'setinfogadget' '"'comment'"' /* always returns result = OK */
- if result = 'OK' then
- 'setcompression' '"'compression'"' /* returns result = OK or FAIL */
- if result ~= 'FAIL' then
- 'setformatting' '"'formatting'"' /* returns current mode, check rc */
- if rc ~= 0 then
- do
- result = 'FAIL'
- end
- else
- 'testarcbits' '"'testbits'"' /* Set "test bits" to either Yes or No */
- if result ~= 'FAIL' then
- do
- /***********************************************
- only test date if we aren't testing archive bits
- ***********************************************/
- if testbits = 'NO' then
- do
- 'dateformat 0'
- settestdate '"'testdate'"' /* returns current date, check rc */
- if rc ~= 0 then result = 'FAIL'
- else result = 'OK'
- end
- end
-
- return result
-
- /*********************************************************************
- error
-
- This function handles an error condition. It exits with
- return code 6, which is 1 higher than a warning. It sends a
- quit to MRBackup.
- *********************************************************************/
- error:
-
- say "Exiting because of error"
- quit /* command to MRBackup to quit */
- exit 6
-
- /*********************************************************************
- break_c
-
- This function exits the ARexx script on a user control-c
- interrupt. It does not stop MRBackup. It exits with return code
- 5, which is a warning.
- *********************************************************************/
- break_c:
-
- say "*** Control-C recieved. Stopped by user. ***"
- /* Note: Do not command to MRBackup to quit. Leave running, assume
- user needs to look at something.
- */
- exit 5
-