[net.sources] The Georgia Tech ICS Backup Program

arnold@gatech.CSNET (Arnold Robbins) (12/17/85)

Here are the programs we use to do our incremental backups. The real backup
program is 'bru' (backup and restore utility)  whose man page is also included
(by permission). If you are interested in it, contact Fred Fish, unisoft!usr68.

This program sits on top of bru, and manages which type of backup to do,
and which tapes to use. This is the second version. The first was 2100 lines
of C; this is about 700 lines of shell with a few very small C programs.
It is amazing what the shell can do.

In a fit of <whatever>, I named it 'beer', as a pun on bru (brew). Please, no
flames. Besides, I'm leaving soon and the mail would probably sit here for
a few weeks before I saw it, anyway.

I am posting it since it provides a useful example of what the shell can do,
and because it can probably be adapted to your regular backup programs.

Enjoy,

Arnold Robbins
gatech!arnold	(but only 'til 12/20)
-------------------------- :1,.d -----------------------------------------
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	Makefile
#	beer.8
#	beer.c
#	beer.ms
#	beerscript.sh
#	bru.1
#	generate.c
#	roman.c
#	tapesets
#	uid.c
#	verify.c
# This archive created: Tue Dec 17 10:17:43 1985
# By:	Arnold Robbins (The Bill the Cat Fan Club)
export PATH; PATH=/bin:$PATH
echo shar: extracting "'Makefile'" '(1530 characters)'
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
cat << \SHAR_EOF > 'Makefile'
# Makefile for beer program

SOURCES = beer.c generate.c roman.c verify.c uid.c beerscript.sh

EXECUTABLES = beer generate roman verify uid beerscript.sh

PR = pr
NROFF = nroff

OWNER = root
GROUP = operator

CFLAGS=-O

all: $(EXECUTABLES)

install: beer
	-test -d /etc/beer || mkdir /etc/beer
	-if [ ! -d /etc/beer/touch ] ; then mkdir /etc/beer/touch ;\
	else rm -f /etc/beer/touch/* ; fi
	-if [ ! -d /etc/beer/log ] ; then mkdir /etc/beer/log ;\
	else rm -f /etc/beer/log/* ; fi
	cp tapesets /etc/beer/tapesets
	/etc/chown $(OWNER) /etc/beer /etc/beer/touch /etc/beer/log
	chgrp $(GROUP) /etc/beer /etc/beer/touch /etc/beer/log
	chmod 775 /etc/beer /etc/beer/touch /etc/beer/log
	chmod 664 /etc/beer/tapesets
	cp beer.8 /usr/man/man8; chmod 644 /usr/man/man8/beer.8
	cp beer.ms /usr/doc
	cp $(EXECUTABLES) /etc/beer
	/etc/chown $(OWNER) /etc/beer/*
	chgrp $(GROUP) /etc/beer/*
	chmod 4711 /etc/beer/beer	# ONLY!!!!

redo: beerscript.sh beer
	cp beerscript.sh /etc/beer
	cp beer /etc/beer
	/etc/chown root /etc/beer/beer
	chmod 4711 /etc/beer/beer

beer: beer.c
	$(CC) $(CFLAGS) $@.c -o $@

generate: generate.c
	$(CC) $(CFLAGS) $@.c -o $@

roman: roman.c
	$(CC) $(CFLAGS) $@.c -o $@

verify: verify.c
	$(CC) $(CFLAGS) $@.c -o $@

uid: uid.c
	$(CC) $(CFLAGS) $@.c -o $@


print:
	$(PR) Makefile $(SOURCES) | lpr

prconfig:
	$(PR) /etc/beer/tapesets | lpr

doc: beer.8 beer.ms
	$(NROFF) -man beer.8 | col | lpr
	$(NROFF) -ms beer.ms | col | lpr

printall: print prconfig doc

clean:
	rm -f $(OBJECTS)

clobber: clean
	rm -f beer
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'beer.8'" '(7318 characters)'
if test -f 'beer.8'
then
	echo shar: will not over-write existing file "'beer.8'"
else
cat << \SHAR_EOF > 'beer.8'
.if n .ds LQ ""
.if n .ds RQ ""
.if t .ds LQ ``
.if t .ds RQ ''
.TH BEER 8 "Georgia Tech ICS"
.SH NAME
beer \-
.BR B ackup
files
.BR E asily,
.BR E ffectively,
and
.BR R eliably
.SH SYNOPSIS
.B /etc/beer/beer
[
.B anything
]
.SH DESCRIPTION
.SS Introduction
.I Beer
is a program designed to automate full and incremental backups, using
.IR bru (1)
to do the actual archiving.
It takes one optional argument, which is used to turn
debugging output on.
The text of the argument is irrelevant,
.I beer
only cares whether or not an argument is there.
.SS Full And Incremental Backups
.PP
.I Full
backups are done quarterly, while
.I incremental
backups are done
monthly, weekly, and daily.
Monthly backups are done relative to the last quarterly,
weekly backups are relative to the last monthly (or quarterly if no monthlies),
and dailies are relative to the last weekly (or monthly, or quarterly).
For example, when doing weekly backups, only files which have
changed since the last monthly or quarterly backup will be dumped to tape.
.PP
.I Beer
automatically determines which kind of backup to do,
and keeps track of which tapes to use for any given kind of
incremental backup.
(It is assumed that quarterly backup tapes are not re-used, but kept
\*(LQpermanently\*(RQ for future reference.)
.SS Tape Sets
.PP
The usual procedure is to have multiple tape sets for each kind of incremental
backup. For instance, between each monthly backup, there should be three
weekly backups. Each of these weeklies will have its own set of tapes.
.I Beer
automatically manages the tape sets, instructing the operator which tape set
to use for this particular backup session.
.PP
For security, it is a good idea to keep an additional tape set
of the more frequent types of backups relative to the less frequent.
For example, the sequence might be as follows:
.sp
.RS
.nf
Q	w	w	w	M	w	w	w	M 	...
	1	2	3	1	4	1	2	2
.fi
.RE
.sp
where \*(LQQ\*(RQ is a quarterly, \*(LQw\*(RQ is a weekly, and \*(LQM\*(RQ
is a monthly.
After the first monthly, weekly sets 1 and 2 can be re-used, but weekly set 3
should be kept until after the second monthly backup has been completed.
.SS Tape Configuration File
.PP
.I Beer
uses one configuration file which controls which tapes it will use.
All other options are built into
.I beer
itself, and are easily changeable, since
.I beer
is just a large shell program.
.PP
The file which
.I beer
uses is
.BR /etc/beer/tapesets .
For each kind of incremental backup, this file lists how many tape sets
there are, and which one to use next.
The initial file is set up for the tape configuration described above, with
four monthly tape sets,
four weekly tape sets,
and
two daily tape sets.
It is updated automatically each time
.I beer
is run.
It should almost never have to be edited by hand.
.PP
In
.BR /etc/beer/tapesets ,
.I beer
will ignore empty lines, and treat any part of a line that begins with a
.B #
as a comment.
The
.B #
cannot be escaped.
.I Beer
will also ignore case distinctions on the information in this file.
.SS Touch Files
.PP
.I Beer
keeps a set of \*(LQtouch\*(RQ files, whose modification time is
used for controlling which files to backup.
These files all reside in the
.B /etc/beer/touch
directory.
For example, since weekly backups are done relative to the last
monthly,
.I beer
instructs
.I bru
to backup all files newer than the file
.B /etc/beer/touch/monthly
(or
.B /etc/beer/touch/quarterly
if no monthlies were done since the last quarterly).
.SS Log Files
.PP
.I Beer
also keeps a set of \*(LQlog\*(RQ files, containing the
.I bru
output from the particular backup.
These files reside in
.BR /etc/beer/log ,
and their names are indicative of which tape set was used, and the date.
The
.I bru
output contains the volume number in square brackets (\fB[\|]\fP),
so, between the file name itself, and the contents of the file, it should
be possible to locate exactly which tape any given user's file is archived on.
(A member of the Lab Staff should be contacted to actually
restore the files, since this requires Root access for best results.)
.PP
.I Beer
will automatically delete the \*(LQtouch\*(RQ and \*(LQlog\*(RQ files
that should not be kept after any particular backup.
For example, after a quarterly dump,
it will delete the monthly and weekly \*(LQtouch\*(RQ files, all the
daily and weekly \*(LQlog\*(RQ files, and all but the last monthly
\*(LQlog\*(RQ file.
.PP
Quarterly dumps are not logged to disk, but are instead piped through
.IR pr (1)
(with an appropriate heading), and then to
.IR lpr (1).
Since printers tend to jam,
.I beer
will ask the operator if it really should send the output to the
printer. If the operator's answer begins with an \*(LQn\*(RQ,
.I beer
will cause
.I bru
to do its work silently.
.SS Automatic Backup Type Selection
.PP
.I Beer
automatically decides what kind of incremental backup to do,
based on the date and day of the week.
If it is the day of the week given for weekly or monthly backups,
and it is within the first day of the month,
.I beer
will do a monthly backup.
Otherwise, it will do a weekly backup.
If it is not the day of the week for monthly and weekly backups,
.I beer
will simply do a daily backup.
.PP
The type of backup that
.I beer
chooses is merely a default;
the operator may over-ride the choice and choose to do a different
kind, as necessary.
.SS Directory Hierarchies
.PP
Incremental backups are done relative to
.BR / ;
when doing quarterlies,
.I beer
will change its current working directory
to each file system in turn, and do the backup relative to \*(LQ.\*(RQ.
This facilitates doing a full restore onto a different file system,
which may be mounted on a different directory while doing the restore.
.SH FILES
.TP
.B /etc/beer
Directory containing
.IR beer 's
configuration file, the
.I beer
shell file, and the necessary subdirectories.
.TP
.B /etc/beer/tapesets
Configuration file containing tape set information.
.TP
.B /etc/beer/touch/*
The directory where the \*(LQtouch\*(RQ files are kept.
.TP
.B /etc/beer/log/*
The directory where the \*(LQlog\*(RQ files are kept.
Log file names are of the form
.sp
.nf
.in +1i
monthly.TS.MM-DD
weekly.TS.MM-DD
daily.TS.MM-DD-DOW
.in -1i
.fi
.sp
where TS is the tape set number,
MM is the month,
and DD is the day.
For daily backups, DOW is an abbreviation of the day of the week
(e.g. \*(LQMon\*(RQ for Monday).
.SH SEE ALSO
.IR bru (1),
.IR pr (1),
.IR lpr (1),
.IR sh (1).
.br
.IR "Doing Backups with BEER" ,
by Arnold Robbins.
.SH DIAGNOSTICS
Self explanatory. Exit codes are as follows:
.TP
0
Normal exit.
.I Beer
ran without any problems.
.TP
1
The person running
.I beer
was not the operator.
.TP
2
No files could be found in the
.B /etc/beer/touch
directory for time stamp comparison.
.TP
3
The operator chose to exit by typing an EOF at a prompt,
instead of continuing with whatever operation
.I beer
was about to do.
.SH BUGS
.PP
The current set up for incrementals is somewhat tape intensive, in
return for increased reliability.
.\" we hope...
.PP
Since it is a shell file,
.I beer
does not do a very good job of error detection or
recovery.
.PP
The acronym given above in the
.SM
.B NAME
section is slightly forced.
.PP
Despite the program's name,
the user must supply his own six-packs.
.SH AUTHOR
Arnold Robbins
.br
arnold@gatech.{CSNET, UUCP}
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'beer.c'" '(417 characters)'
if test -f 'beer.c'
then
	echo shar: will not over-write existing file "'beer.c'"
else
cat << \SHAR_EOF > 'beer.c'
/*
 * beer.c
 *
 * intermediate C program to prevent
 * setuid shell scripts
 */

main (argc, argv)
register int argc;
register char **argv;
{
	setgid (getegid ());
	setuid (geteuid ());
	switch (argc) {
	case 1:
		execl ("/bin/sh", "sh", "/etc/beer/beerscript.sh", (char *) 0);
		break;

	default:
		execl ("/bin/sh", "sh", "/etc/beer/beerscript.sh", argv[1], (char *) 0);
		break;
	}
	perror ("exec");
	exit (1);
}
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'beer.ms'" '(11712 characters)'
if test -f 'beer.ms'
then
	echo shar: will not over-write existing file "'beer.ms'"
else
cat << \SHAR_EOF > 'beer.ms'
.ie t \{ .de CW
.vs 10.5p
.ta 16m/3u 32m/3u 48m/3u 64m/3u 80m/3u 96m/3u
.nf
.ft CW
.cs CW 18
.. \}
.el \{ .de CW
.nf
.. \}
.ie t \{ .de CN
.ta 0.5i 1i 1.5i 2i 2.5i 3i 3.5i 4i 4.5i 5i 5.5i 6i
.vs
.fi
.cs CW
.ft
.. \}
.el \{ .de CN
.fi
.. \}
.if n .ds lq ""
.if n .ds rq ""
.if t .ds lq ``
.if t .ds rq ''
.nr PO 1i
.RP
.TL
Doing Backups with BEER
.AU
Arnold D. Robbins
.AI
School of Information and Computer Science
Georgia Institute of Technology
Atlanta, Georgia  30332
(404) 894-3658
.sp
arnold@gatech.{CSNET, UUCP}
.AB
.I Beer
is the Georgia Tech backup program for doing
.B B ackups
.B E asily,
.B E ffectively,
and
.B R eliably.
This document discusses using
.I beer
in day to day operation.
The manual page
.I beer (8)
should also be consulted.
.AE
.NH
INTRODUCTION
.PP
.I Beer
is a program designed for the management of full and
incremental file system backups of the Georgia Tech Unix machines.
Actual backups are done using the
.I bru (1)
program, which is not discussed here.
.I Beer
keeps track of which file systems are to be backed up,
when they are to be backed up, and which tapes are to be used
for any given backup.
It arranges to run
.I bru ;
the operator should not have to deal directly with
.I bru
when performing backups, other than to mount additional tapes.
.PP
.I Beer
does four kinds of backups: quarterly, monthly, weekly, and daily.
The quarterly backups are
.I full
dumps, i.e. every file on the machine
is dumped to tape.
These are usually done in single-user mode (only the
system console active); the procedure for doing
quarterly backups is described below.
.PP
All other backup types are
.I incremental .
In other words, only files which have been changed since a given time
are dumped to tape.
These are usually done while the system is in multi-user (normal) operation.
.NH
TAPE SET NAMING AND NUMBERING
.PP
Each different type of incremental backup has several sets of tapes.
Having numerous tapes allows for greater redundancy, increasing the
chances that there will be a recent copy of any given file on tape,
should a disk file accidentally be destroyed.
.PP
Tape sets are numbered with Roman numerals, (I, II, III, etc.).
Within the monthly and weekly tape sets the tapes
themselves are numbered with Arabic numerals (1, 2, 3, etc.).
The daily tapes are \*(lqnumbered\*(rq with
the names of the days of the week
(for instance, \*(lqMonday,\*(rq \*(lqTuesday,\*(rq etc.).
.I Beer
automatically keeps track of which tape set to use, asking for tapes
by tape number and tape set
(for example, \*(lqtape 1 from monthly set III,\*(rq
\*(lqTuesday's tape from weekly set II\*(rq).
The tapes in the tape cabinet will be labeled appropriately.
.NH
NORMAL USE (INCREMENTALS)
.PP
In normal, daily use,
.I beer
will select what kind of backup to do; daily, weekly, or monthly.
The operator should simply follow the defaults, which usually means
just hitting the RETURN or NEWLINE key.
.PP
To do backups, the operator should log in to the \*(lqoperator\*(rq
account, and issue the command:
.DS
.CW
% /etc/beer/beer
.CN
.DE
.I Beer
will produce the following prompt:
.DS
.CW
Please enter type of backup.
Only the first letter is significant.
Case is ignored.

Types of backups are:
	Quarterly
	Monthly
	Weekly
	Daily
Choice (default: d) ?
.CN
.DE
In this case, the default is
.B d
for
.B daily .
It will occasionally be
.B w
for
.B weekly ,
or
.B m
for
.B monthly .
.PP
Typing a RETURN or NEWLINE will cause
.I beer
to default to doing the daily backup. The operator may type one of the
other letters to do one of the other kinds of backups.
Typing a Control-D (End-of-file) will cause
.I beer
to exit.
Anything else will cause
.I beer
to re-issue the prompt and wait for a new response.
.PP
.I Beer
proceeds to find the files that should be
backed up to tape.
While it is finding files, it will print the message:
.DS
.CW
Finding files ....
.CN
.DE
When it has found the files, and saved the list in a temporary file,
it will so indicate by saying it is ready to dump the files to tape:
.DS
.CW
Finding files .... done. Ready to dump to tape.
.CN
.DE
.PP
Next,
.I beer
will double check that the default device is correct, with the
following prompt:
.DS
.CW
Enter device (default: /dev/rmt8):
.CN
.DE
.I Beer
indicates that the device to do backups on is the rewinding
tape drive.
Usually, the operator should just type RETURN to continue.
However, should the tape drive be broken, and the operator wish to
do backups on a remote system's tape drive, he should enter that
device name (e.g.
.if n \fBstratus:/dev/rmt8\fP).
.if t \f(CWstratus:/dev/rmt8\fP).
A Control-D can be used to exit,
or the RETURN key to just use the default.
.PP
When using a device other than the default,
.I beer
will ask what physical block size it should use.
In particular, on remote tape drives, the block size
should be no more than 10K.
.DS
.CW
Enter new block size (default: 20K)
.CN
.DE
The default is 20K blocks. Just type RETURN
to continue, or as always, an EOF to exit.
.PP
Finally,
.I beer
will prompt the operator to mount the first tape: (these are examples)
.DS
.CW
Put Tuesday's tape from daily tape set I on the drive.
Mount additional tapes when prompted by bru.
hit return when ready (EOF to exit):
.CN

	- or -

.CW
Put tape 1 of monthly tape set II on the drive.
Mount additional tapes when prompted by bru.
hit return when ready (EOF to exit):
.CN
.DE
The first prompt is an example of what
.I beer
prints for daily backups, while the second is what
.I beer
prints for weekly or monthly backups.
.PP
The operator should find the appropriate tape, and mount it on the
tape drive. The tape should have a write ring in it.
Once the tape is threaded properly, the \*(lqload\*(rq and
\*(lqon line\*(rq buttons should be pressed, in that order.
Then type RETURN on the terminal running
.I beer .
.PP
At this point
.I bru
will start to run.
For weekly and monthly backups,
it will probably require more than one tape.
In this case
.I bru
will prompt for the next
tape to be loaded.
Take the first tape off the drive, mount, thread, and load the next
tape, and then put the drive back on line. Then hit RETURN.
.I Bru
should continue its backups.
Put the current date on the tape label of the first tape,
to indicate when it was last
used for backups.
Repeat this procedure for each volume (tape) of the backup.
.NH
QUARTERLY BACKUPS
.PP
Quarterly backups are a little more involved than incremental backups.
Therefore, running
.I beer
is more complicated.
This sections discusses the procedures to follow, and some of the
reasons why the procedures are set up as they are.
.PP
Quarterlies are usually done in single-user mode, i.e., with no one
else using the system. This is to guarantee that the file systems are
quiescent (quiet), so that
.I bru
will be able to backup all the files, without worrying that someone may
be in the middle of modifying his or her files.
.PP
The System Administrator or another member of the Lab Staff will be
responsible for bringing the system down to single-user: The operator
should not worry about that.
Once the system is in single-user mode,
.I beer
can be run to do the backups.
.PP
We present a sample
.I beer
session, explaining the options available at each point, and the
reasoning behind them.
Quarterly backups start out the same as usual:
.DS
.CW
% /etc/beer/beer

Please enter type of backup.
Only the first letter is significant.
Case is ignored.

Types of backups are:
	Quarterly
	Monthly
	Weekly
	Daily
Choice (default: d) ?
.CN
.DE
This time however, the answer is \*(lqq\*(rq, for quarterly.
.I Beer
proceeds to prompt with:
.DS
.CW
Current file system is \fIfile system\fP. Skip? (n)
.CN
.DE
.I Beer
is ready to dump the given \fIfile system\fP.
The easiest thing to do is hit RETURN, which will continue with the backup.
Use \*(lqy\*(rq (case does not matter) to skip this file system,
and go on to the next one. This is particularly useful if backups are
taking more than one day, and the file system in question was already
backed up earlier.
An EOF (Control-D) will exit.
.PP
Once you've hit RETURN,
.I beer
will prompt with:
.DS
.CW
Enter device (default: /dev/rmt8):
.CN
.DE
This is the same as for incremental backups. See the discussion above.
This option will be most useful to use the non-rewinding tape drive
on small file systems; it may be possible to put up to four small
file systems on a single 2400 foot tape.
Should a new device be chosen,
.I beer
will also prompt for a different block size, as described above.
.PP
At the discretion of the System Administrator, the skipping of
file systems and the use of alternate devices can be used together,
by having
.I beer
running on more than one terminal.
Then, one session can be doing one group of file systems on the
local tape drive, while another session can be doing a different group
on a remote drive.
Doing multiple backups should speed up the process somewhat.
These two options allow this possibility, without having to change
the
.I beer
shell source code.
(The System Administrator can arrange to create a terminal session
on a terminal besides the system console when the system is in single-user
mode.)
.PP
Next,
.I beer
prompts with:
.DS
.CW
Send output to the line printer? (y)
.CN
.DE
A reply starting with an upper- or lower-case \*(lqn\*(rq will
tell
.I beer
to have
.I bru
do the backup silently. Any other non-empty response (\*(lqyes,\*(rq
\*(lqdrop dead,\*(rq etc.) will cause
.I beer
to go ahead and print the
.I bru
output.
Hitting RETURN will also send the
.I bru
output to the printer.
Typing an EOF (Control-D) will  exit.
.PP
It may be necessary to not print the
.I bru
output, since the printer tends to jam on long printouts.
(What may happen is that the printout for the first file system jams
the printer, so on subsequent file systems, printouts should not be done.)
.PP
Finally,
.I beer
gets down to work with:
.DS
.CW
Put the first tape for \fIfile system\fP on the drive.
Mount additional tapes when prompted by bru.
hit return when ready (EOF to exit):
.CN
.DE
.I "File system"
will be the name of the particular file system being backed up,
for instance,
.B /u/staff .
Put the first tape on the drive, thread it, load it, and then put the
drive on-line.
Then hit RETURN to proceed with the backup.
As
.I bru
needs additional tapes, it will request them:
mount them in turn.
.PP
Each tape should have two labels.
The first label should look like:
.DS
Quarterly Dump - \fIQuarter Year\fP
\fIFile system\fP - Tape \fIN\fP of \fIM\fP
.DE
where
.I quarter
is the quarter just ending (e.g. Summer), and 
.I year
is the year.
.I "File system"
is the name of the file system just dumped.
The label will also indicate that the tape is
.I N
of
.I M ,
for instance, 2 of 4.
(Usually the tapes are labeled \*(lq2 of\*(rq, with the \*(lq4\*(rq
being filled in after the entire file system is done, and the total
number of tapes are known.)
.PP
The second tape label is the same for all the tapes:
.DS
Vax 11/780 - BRL Unix 3.0 (4.2 BSD)
BRU Format
20K Blocks
.DE
which describes the machine, operating system, and block size used to
create the tape.
If a different device was used, with a different block size,
note the block size on the label instead of the \*(lq20K\*(rq.
.PP
Finally, one of the thin edge labels should be placed on the tape ring,
and the quarter, year, file system, and tape number written on the tape,
so that once the tape is placed in a tape rack, it is clear what the
contents of the tape are (for instance, \*(lqSummer 85 - /usr - 2\*(rq).
.NH
FURTHER INFORMATION
.PP
More information about 
.I beer
can be found in the
.I beer (8)
manual page, the file
.B /etc/beer/tapesets ,
and, of course,
the C and shell source code.
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'beerscript.sh'" '(10337 characters)'
if test -f 'beerscript.sh'
then
	echo shar: will not over-write existing file "'beerscript.sh'"
else
cat << \SHAR_EOF > 'beerscript.sh'
#! /bin/sh

# beer -- Backup files Easily, Effectively, and Reliably
#
# rewritten from the C version, must be setuid to root
#
# Arnold Robbins, 11/85
# gatech!arnold

# this program makes heavy use of the 'test' command, via its
# alias of '['. On regular BSD, this might slow it down considerably,
# however, BRL modified the shell to make 'test' and '[' built-in
# commands.

PATH=/etc/beer:/bin:/usr/bin:/usr/ucb:/usr/brl/bin:/usr/local/bin

# turn on debugging if any arguments present

if [ $# != 0 ]
then
	debug=yes
	uid
else
	debug=
fi

# get the date, and set verbose versions

set `date`
case $1 in
Mon)	weekday=monday
	yesterday=sunday
	;;
Tue)	weekday=tuesday
	yesterday=monday
	;;
Wed)	weekday=wednesday
	yesterday=tuesday
	;;
Thu)	weekday=thursday
	yesterday=wednesday
	;;
Fri)	weekday=friday
	yesterday=thursday
	;;
Sat)	weekday=saturday
	yesterday=friday
	;;
Sun)	weekday=sunday
	yesterday=saturday
	;;
esac

shortday=$1

case $2 in
Jan)	month=1 ;;
Feb)	month=2 ;;
Mar)	month=3 ;;
Apr)	month=4 ;;
May)	month=5 ;;
Jun)	month=6 ;;
Jul)	month=7 ;;
Aug)	month=8 ;;
Sep)	month=9 ;;
Oct)	month=10 ;;
Nov)	month=11 ;;
Dec)	month=12 ;;
esac

day=$3

if [ "$debug" ]
then
	echo weekday = $weekday
	echo yesterday = $yesterday
	echo shortday = $shortday
	echo month = $month
	echo day = $day
fi

nondaily=monday		# MUST be in lower case

# determine default backup type

if [ "$nondaily" = "$weekday" ]
then
	if [ "$day" -ge 1 -a "$day" -le 7 ]
	then
		backup=monthly
		prompt=m
	else
		backup=weekly
		prompt=w
	fi
else
	backup=daily
	prompt=d
fi

if [ "$debug" ]
then
	echo nondaily = $nondaily
	echo default backup = $backup
	echo prompt = $prompt
fi

while :		# infinite loop
do
	cat << EOF
Please enter type of backup.
Only the first letter is significant.
Case is ignored.
Types of backups are:
	Quarterly
	Monthly
	Weekly
	Daily
EOF
	echo -n "Choice (default: $prompt) ? "
	if read answer
	then
		case "$answer" in
		[Dd]*)	backup=daily
			break
			;;
		[Ww]*)	backup=weekly
			break
			;;
		[Mm]*)	backup=monthly
			break
			;;
		[Qq]*)	backup=quarterly
			break
			;;
		"")	break	# took the default
			;;
		*)	echo "$answer": not a valid response
			;;
		esac
	else
		exit 3	# operator typed an EOF
	fi
done

if [ "$debug" ]
then
	echo backup type is $backup
fi

operator=operator	# MUST be in lower case

if verify $operator
then
	if [ "$debug" ]
	then
		echo operator verified
	fi
else
	echo you are not $operator! >&2
	exit 1
fi

# now set up which file to compare for being newer than

case "$backup" in
daily)		if test -f /etc/beer/touch/weekly
		then
			newest=/etc/beer/touch/weekly
		elif test -f /etc/beer/touch/monthly
		then
			newest=/etc/beer/touch/monthly
		elif test -f /etc/beer/touch/quarterly
		then
			newest=/etc/beer/touch/quarterly
		else
			echo no files in /etc/beer/touch to compare with! >&2
			exit 2
		fi
		;;

weekly)		if test -f /etc/beer/touch/monthly
		then
			newest=/etc/beer/touch/monthly
		elif test -f /etc/beer/touch/quarterly
		then
			newest=/etc/beer/touch/quarterly
		else
			echo no files in /etc/beer/touch to compare with! >&2
			exit 2
		fi
		;;

monthly)	if test -f /etc/beer/touch/quarterly
		then
			newest=/etc/beer/touch/quarterly
		else
			echo no files in /etc/beer/touch to compare with! >&2
			exit 2
		fi
		;;
esac

if [ "$debug" ]
then
	echo newest "(file to compare against)" is $newest
fi

# Now find out, for the given backup type, how many tapesets there are,
# and which one to use next. First, fix the file to all lower case.

tr A-Z a-z < /etc/beer/tapesets | sed 's/#.*//' > /tmp/$$.tapesets
	
# now save all the info

set `grep "^daily" /tmp/$$.tapesets`
dailyamt=$2
dailywhich=$3

set `grep "^weekly" /tmp/$$.tapesets`
weeklyamt=$2
weeklywhich=$3

set `grep "^monthly" /tmp/$$.tapesets`
monthlyamt=$2
monthlywhich=$3

if [ "$backup" != quarterly ]
then
	# doing an incremental backup

	# get which tapeset to use now, and save the info
	
	set `grep "^$backup" /tmp/$$.tapesets`
	
	howmany=$2	# number of tapes
	whichtouse=$3	# which set to use now
	
	if [ "$debug" ]
	then
		echo howmany = $howmany
		echo which to use = $whichtouse
	fi
	
	# first, decide if necessary to rewrite the file
	# we rewrite the file for weekly and monthly backups.
	# if a daily backup, rewrite the file only if weeklies were
	# done yesterday.
	
	changesets=yes
	if [ "$backup" = daily -a "$yesterday" != "$nondaily" ]
	then
		changesets=
	fi
	
	if [ "$changesets" ]
	then
		if [ "$debug" ]
		then
			echo changing tape sets
			cat /etc/beer/tapesets
		fi
	
		# redo the file, if necessary
	
		nextup=`expr $whichtouse + 1`	# only call expr once

		if [ "$nextup" -gt "$howmany" ]
		then
			newwhich=1
		else
			newwhich=$nextup
		fi
	
		# save the values, so they can be restored for later use
		olddaily=dailywhich
		oldweekly=weeklywhich
		oldmonthly=monthlywhich

		case "$backup" in
		daily)		dailywhich=$newwhich ;;
		weekly)		weeklywhich=$newwhich ;;
		monthly)	monthlywhich=$newwhich ;;
		esac
	
		# rewrite the file (kerblam!)
		cat << EOF > /etc/beer/tapesets
# /etc/beer/tapesets
#
# This file describes how many sets of tapes are available for a
# particular type of backup, and which set to use the next time
# beer is called. While it may edited, beer will recreate it
# each time it is run, automatically updating the numbers.
#
# type		how many	which to use next
#
monthly		$monthlyamt		$monthlywhich

weekly		$weeklyamt		$weeklywhich

daily		$dailyamt		$dailywhich
EOF
		if [ "$debug" ]
		then
			cat /etc/beer/tapesets
		fi

		# restore the values, to use later when removing
		# unneeded log files

		dailywhich=olddaily
		weeklywhich=oldweekly
		monthlywhich=oldmonthly
	fi

	# now set up the logfile name

	dir=/etc/beer/log
	case "$backup" in
	daily)		logfile=$dir/$backup.$whichtouse.$month-$day-$shortday ;;
	weekly|monthly)	logfile=$dir/$backup.$whichtouse.$month-$day ;;
	esac

	if [ "$debug" ]
	then
		echo logfile is $logfile
	fi
fi

# now determine which files to backup

case "$backup" in
daily|weekly)	special="/etc /dev /lib /bin"
		regular="/usr/local /usr/spool/mail /u"
		;;
monthly)	special="/etc /dev /lib /bin"
		regular="/usr /u"
		;;
quarterly)	all="/ /usr /usr/man /usr/doc /usr/spool /usr/ingres /u /u/ai /u/staff /u/vlsi /u/graphics /u/graphics/pix /usr/src/csnet"
		;;
esac

if [ "$backup" != quarterly ]
then
	if [ "$debug" ]
	then
		echo finding files
	fi

	echo -n Finding files ....

	find $special -newer $newest -print > /tmp/$$.list
	find $regular -type f -newer $newest -print | grep -v '\.o$' |
		grep -v '/,[^/]*$' | grep -v '/core$' >> /tmp/$$.list

	echo done. Ready to dump to tape.

	if [ "$debug" ]
	then
		echo found `wc -l /tmp/$$.list` files to be dumped
	fi

	bruopts="-cavv"

	echo -n "Enter device (default: /dev/rmt8) "
	if read dev
	then
		if [ "$dev" ]
		then
			bruopts="$bruopts -f $dev"
			echo -n "Enter new block size (default: 20K) "
			if read blocksize
			then
				if [ "$blocksize" ]
				then
					bruopts="$bruopts -b $blocksize"
				fi
			else
				exit 3
			fi
		fi
	else
		exit 3
	fi

	# now put a label in
	bruopts="$bruopts -L '$backup backup - `date`'"

	if [ "$debug" ]
	then
		echo bruopts = $bruopts
	fi

	case "$backup" in
	daily)	echo -n Put "${weekday}'s" tape from daily tape set
		echo `roman $whichtouse` on the drive.
		;;
	weekly)	echo Put tape 1 of $backup tape set `roman $whichtouse`
		echo on the drive.
		;;
	monthly) echo Put tape 1 of $backup tape set `roman $whichtouse`
		echo on the drive.
		;;
	esac
	echo Mount additional tapes when prompted by bru.
	echo -n "hit return when ready (EOF to exit): "

	if read junk
	then
		# use eval to get quoting etc. to work
		eval "bru $bruopts - < /tmp/$$.list > $logfile"
	else
		exit 3
	fi
else
	# quarterly backups

	for fs in $all
	do
		bruopts="-cm"	# don't use -a for quarterlies

		echo -n Current file system is $fs. Skip? "(n) "
		if read ans
		then
			case "$ans" in
			[Yy]*)	continue ;;
			esac
		else
			exit 3
		fi

		echo -n "Enter device (default: /dev/rmt8) "
		if read dev
		then
			if [ "$dev" ]
			then
				bruopts="$bruopts -f $dev"
				echo -n "Enter new block size (default: 20K) "
				if read blocksize
				then
					if [ "$blocksize" ]
					then
						bruopts="$bruopts -b $blocksize"
					fi
				else
					exit 3
				fi
			fi
		else
			exit 3
		fi

		echo -n Send output to printer? "(y) "
		if read ans
		then
			case "$ans" in
			[Nn]*)	output=
				;;
			*)	output=" | pr -h 'quarterly dump of $fs - `date`' | lpr"
				bruopts="-vv $bruopts"
				;;
			esac
		else
			exit 3
		fi

		# now put a label in
		bruopts="$bruopts -L 'quarterly backup - $fs - `date`'"

		if [ "$debug" ]
		then
			echo bruopts = $bruopts
			echo backing up $fs
			echo output = "$output"
		fi

		cd $fs

		echo Put the first tape for $fs on the drive.
		echo Mount additional tapes when prompted by bru.
		echo -n hit return when ready "(EOF to exit): "
		if read junk
		then
			# we must use eval, to get possible pipes to work right
			eval "bru $bruopts . $output"
		else
			exit 3
		fi
	done
fi

# now update the touch files

case "$backup" in
daily)	;;
*)	echo /etc/beer/touch/$backup last updated on `date` > /etc/beer/touch/$backup
	;;
esac

# then delete the old touch files that aren't needed anymore
case "$backup" in
quarterly)	rm -f /etc/beer/touch/monthly /etc/beer/touch/weekly
		;;
monthly)	rm -f /etc/beer/touch/weekly
		;;
esac

# delete all the appropriate log files

case "$backup" in
quarterly)	# all daily and weekly logs, all monthly but last
		rm -f /etc/beer/log/daily* /etc/beer/weekly*
		case "$monthlywhich" in
		1)	save=$monthlyamt ;;
		*)	save=`expr $monthlywhich - 1` ;;
		esac
		for i in `generate 1 $monthlyamt`
		do
			if [ $i -ne $save ]
			then
				if [ "$debug" ]
				then
					echo removing /etc/beer/log/monthly.${i}*
				fi
				rm -f /etc/beer/log/monthly.${i}*
			fi
		done
		;;

monthly)	# all daily logs, all weekly but last
		rm -f /etc/beer/log/daily*
		case "$weeklywhich" in
		1)	save=$weeklyamt ;;
		*)	save=`expr $weeklywhich - 1` ;;
		esac
		for i in `generate 1 $weeklyamt`
		do
			if [ $i -ne $save ]
			then
				if [ "$debug" ]
				then
					echo removing /etc/beer/log/weekly.${i}*
				fi
				rm -f /etc/beer/log/weekly.${i}*
			fi
		done
		;;

weekly)		# all daily logs
		rm -f /etc/beer/log/daily*
		;;
esac

rm -f /tmp/$$.tapesets /tmp/$$.list # /tmp/$$.beercan

exit 0
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'bru.1'" '(24278 characters)'
if test -f 'bru.1'
then
	echo shar: will not over-write existing file "'bru.1'"
else
cat << \SHAR_EOF > 'bru.1'
.\" 
.\" $Header: bru.1,v 1.1 85/11/25 13:03:53 arnold Exp $
.\" 
.\" $Log:	bru.1,v $
.\" Revision 1.1  85/11/25  13:03:53  arnold
.\" Initial revision
.\" 
.\" 
.\" 
.\"	@(#)bru.1	5.6	10/1/85
.\"
.\"	BRU (backup and restore utility)
.\"	Nroff/Troff manual page source
.\"
.\"	tbl bru.1 | nroff -man >bru.man
.\"	tbl bru.1 | troff -man | ...
.\"
.if n .ds lq ""
.if n .ds rq ""
.if t .ds lq ``
.if t .ds rq ''
.if n .ds BU o
.if t .ds BU \(bu
.de TS
.RS 15
..
.de TE
.RE
..
.ad	
.TH BRU 1
.SH NAME
bru \- backup and restore utility
.SH SYNOPSIS
.B bru\^
.B modes\^
[
.B control options
]
[
.B selection options
]
files
.SH DESCRIPTION
.ad
.I Bru\^
is a
.B Unix\^
filesystem backup utility with significant enhancements
over other more common utilities such as
.IR tar ,
.IR cpio ,
.IR volcopy ,
and
.IR dd .
Some of 
.IR bru 's
capabilities include:
.sp 1
.RS 5
.PD 0
.TP 3
\*(BU
Full or incremental backup with quick and easy restoration
of files.
.TP 3
\*(BU
Multiple physical volumes per archive.
.TP 3
\*(BU
Data integrity assurance via checksum computation on every
archive block.
.TP 3
\*(BU
Ability to properly save and restore directories,
symbolic links,
block special files, and character special files.
.TP 3
\*(BU
Comparison of archives with current directory hierarchy.
.TP 3
\*(BU
Ability to recover files from corrupted archives or damaged media
with minimal data loss.
.TP 3
\*(BU
No inherent maximum archive buffer size.
.TP 3
\*(BU
Improved performance through random access archive I/O when
available.
.TP 3
\*(BU
Automatic byte or half word swapping as necessary when reading
archives produced on other machines.
.TP 3
\*(BU
Recognition of filename generation patterns in the same
form as the shell for files read from an archive.
.RE
.PD 1
.PP
When 
.I files\^
are specified on the command line then the actions to be
performed are limited to those
.IR files .
If a named file is a directory then it and all its descendents
are used.
If no
.I files\^
are specified then the default for writing archives is all files
in and below the current directory.
The default for reading archives is selection of all files in
the archive.
.PP
If \*(lq\-\*(rq is given instead of
.I files\^
then the standard input is read to obtain the file list.
This is useful in conjunction with the 
.I find\^
command to provide finer control over files selected for
backup.
Obviously this mode is only valid when 
.I bru\^
is not also reading its archive from the standard input.
.SH DEFAULTS
.ad
Various default parameters, such as archive device name
and size, archive buffer size, controlling terminal name,
etc. are system dependent.
These defaults, along with version, variant, and other
miscellaneous internal  information may be discovered
via the
.B \-h
mode.
.SH MODES
.ad
One or more of the following modes must be specified.
The order of execution, from highest priority to lowest, is 
.BR ecitxdh .
.TP 10
.B \-c
.B Create\^
a new archive.
Forces a new archive to be created regardless of whether one
currently exists.
Writing starts at the first block.
.TP
.B \-d
.B Differences
between archived
.I files\^
and current
.I files\^
are detected and reported.
May be specified more than once, as
.B \-dd\^
.B \-ddd\^
or
.B \-dddd\^
to control level of difference checking.
.br
.sp
When specified as
.B \-d\^
.I bru\^
reports when it discovers that a regular file's size (st_size) or contents
(when compared as byte streams) has changed since the archive
was made.
.br
.sp
When specified as
.B \-dd\^
.I bru\^
reports additional differences in
modification date (st_mtime)
access mode (st_mode),
number of links (st_nlink) for non-directory files,
differences in the contents of symbolic links,
owner id (st_uid),
and
group id (st_gid).
.br
.sp
When specified as
.B \-ddd\^
.I bru\^
reports additional differences in
host device (st_dev),
major/minor device (st_rdev) for special files,
and
time of last access (st_atime) for regular files.
.br
.sp
When specified as 
.B \-dddd\^
.I bru\^
reports all differences except
time of last status change (st_ctime is not resettable),
major/minor device numbers for non-special files (meaningless),
and
size differences for directory files (may have empty entries).
The
.B \-dddd\^
mode is generally only meaningful during a verification
pass with full backups of
quiescent filesystems.
.TP
.B \-e
.B Estimate\^
media requirements for archive creation with same arguments.
Prints estimated number of volumes, number of files to be
archived, total number of archive blocks, and total size
of archive in kilobytes.
If the media size is unknown or unspecified,
it is assumed to be infinite.
.TP
.B \-h
Print 
.B help\^
summary of options.
Also prints some internal information such as version number
and default values for archive pathname, media size,
archive buffer size, etc.
.TP
.B \-i
.B Inspect\^
archive for internal consistency and data integrity.
When
.B \-vv\^
option is also given, prints information from archive
header block.
.TP
.B \-t
List
.B table\^
of contents of archive.
When used with the
.B \-v\^
option will give a verbose table of contents in the same format as
the \*(lqls \-l\*(rq command.
When used with the
.B \-vv\^
option will also indicate what files are linked to other files,
and where symbolic links point to.
.TP
.B \-x
.B Extract\^
named
.I files\^
from archive.
If an archived file is extracted (see 
.B \-u\^
option) then the
access mode,
device id (special files only),
owner uid,
group uid,
access time,
and
modification time
are also restored.
If the
.B \-C
flag is given (see below),
then the owner uid and group uid will be changed to that of the current user.
.br
.sp
Nonexistent directories are recreated from archived directories
if possible, otherwise they are created with appropriate defaults
for the current user.
Extracted or created directories are initially empty.
.SH CONTROL OPTIONS
.ad
Many of the control options are similar in function to their
.I tar\^
or
.I cpio\^
equivalents.
.PP
Sizes are specified in bytes.
The scale factors
.BR M ,
.BR k ,
or
.BR b ,
can be used to indicate
multiplication by 2**20, 1024, or 512 respectively.
Thus
\*(lq10k\*(rq,
\*(lq20b\*(rq,
and
\*(lq10240\*(rq all specify the same number of
bytes.
.TP 10
.BI "\-# " str\^
Use string
.I str\^
as a control string for the built in debugging system.
This option provides information about the internal workings
of
.I bru\^
for the software maintainer or the merely curious.
Some examples are given later.
.TP
.B \-a
Do not reset the
.B access\^
times of disk files that have
been read while performing other actions.
Normally
.I bru
restores the access and modification times of disk files
after they have been read.
Resetting the times prevents defeat of the mechanism used to track
down and remove
\*(lqdead\*(rq files that haven't been accessed in any meaningful way recently.
.TP
.BI "\-b " bsize\^
Use
.I bsize\^
as the archive input/output
.B buffer\^
size.
The minimum is the size of an archive block (2k or 2048 bytes)
and the maximum is determined by available
memory and I/O device limitations.
If
.I bsize\^
is not an even multiple of 2048 bytes, it will be rounded up.
Normally this option is only required with the
.B \-c\^
mode
since 
.I bru\^
writes this information in the archive header block.
If specified,
.I bsize\^
overrides any existing default value (generally 20k), whether built in or
read from the archive header.
.TP
.B \-B
Useful in shell scripts where
.I bru\^
is run in the
.B background
with no operator present.
Under these conditions,
.I bru\^
simply terminates with appropriate error messages and status,
rather than attempting interaction with the terminal.
.TP
.B \-C
Change the owner
.RB ( chown )
and group of each extracted file to the owner uid and group gid
of the current user.
Normally,
.I bru\^
will restore the owner and group to those recorded in the archive.
This flag causes bru to follow the system default,
with extracted files having the same owner and group as
the user running
.IR bru\^ ,
including Root.
(Under 4.2\ BSD, the default group is that of the directory in
which the file is created.)
.br
.sp
The
.B \-C
option is useful with archives imported from other systems.
In general, it should not be used by the operator or system
administrator when restoring saved files.
Use the
.B \-tv\^
option to see the owner and group of files stored in the archive.
.TP
.BI "\-f " path\^
Use
.I path\^
as the archive
.B file\^
instead of the default.
If the 
.I path\^
is \*(lq\-\*(rq then
.I bru\^
uses the standard input for archive reading or standard output
for archive writing, as appropriate.
.TP
.BI \-F
.B Fast\^
mode.
In fast mode, checksum computations and comparisons are disabled.
This mode is useful when the output of one
.I bru\^
is piped to the input of another
.IR bru\^ ,
or when the data integrity of the archive transmission medium is
essentially perfect.
Archives recorded with fast mode enabled must also be read with
fast mode.
Also, be aware that some of the automatic features of
.IR bru\^ ,
such as automatic byte swapping,
are not functional in fast mode.
.TP
.BI "\-L " str\^
.B Label\^
the archive with the specified string
.IR str .
.I Str\^
is limited to 63 characters and is usually some
meaningful reminder pertaining to the archive contents.
.TP
.B \-l
Ignore unresolved
.BR links\^ .
Normally bru reports problems with unresolved links
(both regular and symbolic links).
This option suppresses all such complaints.
.TP
.B \-m
Do not cross
.B mounted\^
file system boundaries during expansion
of explicitly named directories.
This option applies only to directories named in 
.IR files .
It limits selection of directory descendents to those
located on the same filesystem as the explicitly named directory.
This option currently applies only to the
.B \-c\^
and
.B \-e\^
modes.
.TP
.B \-p
.B Pass\^
over files in archive by reading rather than seeking.
Normally 
.I bru\^
will use random access capabilities if available.
This option forces reading instead of seeks.
.TP
.BI "\-s " msize\^
Use
.I msize\^
as the media
.BR size .
The effective media
.B size\^
will be computed from
.I msize\^
since it must be integral multiple of the input/output buffer
size (see the
.B \-b\^
option).
Normally this option is only required with the
.B \-c\^
mode
since 
.I bru\^
writes this information in the archive header block.
If specified,
.I msize\^
overrides any existing default value, whether built
in or read from the archive header.
.TP
.B \-v
Enable 
.B verbose\^
mode.
May be specified more than once, as
.BR \-vv\^ ,
.BR \-vvv\^ ,
or
.BR \-vvvv\^ ,
to get even more verbosity.
.TP
.B \-w
.B Wait\^
for confirmation.
.I Bru\^
will print the file name, the action to be taken, and 
.B wait\^
for
confirmation.
Any response beginning with 'y' will cause the action to complete.
Any other response will abort the action.
.SH FILE SELECTION OPTIONS
.ad
The file selection options control which files are selected for processing.
Note that some options are only valid with specific modes.
.TP 10
.BI "\-n " date\^
Select only files
.B newer\^
than
.IR date.
The
.I date\^
is given in one of the forms:
.sp
.TS
l l.
DD\-MMM\-YY\^[,HH:MM:SS]	EX:\ 12\-Mar\-84,12:45:00
MM/DD/YY\^[,HH:MM:SS]	EX:\ 3/12/84
MMDDHHMM\^[YY]	EX: 0312124584
pathname	EX: /etc/lastfullbackup
.TE
.IP "" 10
The time of day is optional in the first two forms.
If present, it is separated from the date with a comma.
.IP "" 10
If
.I date\^
is really the pathname of a file,
then the modification date of that file will be used instead.
This is useful in automated backups when a dummy file is
\*(lqtouched\*(rq to save the date of last backup.
.TP
.BI "\-o " user\^
Select only files
.B owned\^
by
.IR user .
.I User\^
may be specified in one of three ways:
.sp 1
.RS 15
.PD 0
.TP 3
\*(BU
As an ascii string corresponding to a user name
in the password file.
.TP 3
\*(BU
As the pathname of a file in which case the owner
of that file is used.
.TP 3
\*(BU
As a numeric value (decimal).
.RE
.PD 1
.TP
.BI "\-u " flags\^
When used in conjunction with
.B \-x\^
mode, causes files of type specified by
.I flags\^
to be
.B unconditionally\^
selected regardless of modification times.
Normally 
.I bru\^
will not overwrite (supersede) an existing file with an older archive
file of the same name.
Files which are not superseded will give warnings if 
.B verbose\^
mode level 2 
.RB ( \-vv )
or higher is enabled.
Possible characters for 
.I flags\^
are:
.sp 1
.RS 15
.PD 0
.TP 5
.B b
select block special files
.TP 5
.B c
select character special files
.TP 5
.B d
select directories
.TP 5
.B l
select symbolic links
.TP 5
.B p
select fifos (named pipes)
.TP 5
.B r
select regular files
.RE
.PD 1
.IP "" 10
Selection of directories only implies that their attributes
may be modified.
Existing directories are never overwritten,
this option merely
allows their attributes to be set back to some
previously existing state.
.IP "" 10
Selection of symbolic links only implies that the contents of the link
will be modified.  It is currently impossible under 4.2\ BSD
to change access time, modification time, or the file mode of a symbolic
link.
.\" This seems to me to be a big bug with symbolic links. ADR.
.SH EXAMPLES
.ad
Create
.RB ( \-c )
a new archive of all files under \*(lq/usr/src\*(rq,
writing archive to file
.RB ( \-f )
\*(lq/dev/rmt0\*(rq
using multiple tapes with a maximum size
.RB ( \-s )
of 30 megabytes per tape.
.PP
.RS
bru \-c \-f /dev/rmt0 \-s 30M /usr/src
.RE
.PP
Create
.RB ( \-c )
a new archive on the default device in the
first pass, archiving all files in and below the current directory
which have been created or modified
.RB ( \-n )
since 3 P.M. on 14\-Jan\-84.
Then do a second pass to verify that there are no differences
.RB ( \-d )
between the archive and current files.
Each file is listed
.RB ( \-v )
as it is processed.
.PP
.RS
bru \-cvd \-n 14\-Jan\-84,15:00:00
.RE
.PP
Archive all files owned
.RB ( \-o )
by user \*(lquser1\*(rq
using the default archive device.
.PP
.RS
find / \-user user1 \-print \|\(bv bru \-c \-
.br
bru \-c \-o user1 /
.RE
.PP
Copy a directory hierarchy from \*(lq/usr/u1\*(rq to
\*(lq/usr/u2\*(rq.
.PP
.RS
(cd /usr/u1; bru \-cf \- ) \|\(bv (cd /usr/u2; bru \-xf \-)
.RE
.PP
Extract
.RB ( \-x )
the regular file \*(lq/usr/guest/myfile\*(rq unconditionally
.RB ( \-ur )
from an archive on file
.RB ( \-f )
\*(lq/dev/rf0\*(rq.
Since the device size was recorded in the header block,
it need not be specified.
Note that option arguments do not need to be
separated from their corresponding option flag by whitespace.
.PP
.RS
bru \-x \-ur \-f/dev/rf0 ./usr/guest/myfile
.RE
.PP
Extract
.RB ( \-x )
all C source files in \*(lq/usr/src/cmd\*(rq that have names
beginning with characters 'a' through 'm'.
Wait
.RB ( \-w )
for confirmation before extracting each file.
.PP
.RS
bru \-xw \(fm/usr/src/cmd/[a\-m]*.c\(fm
.RE
.PP
Inspect
.RB ( \-i )
a previously created archive on the
default device, dumping the contents of the header block for
inspection 
.RB ( \-vvv )
and verifying internal consistency and data integrity
of the archive.
.PP
.RS
bru \-ivvv
.RE
.PP
Perform the same function as the last example
except enable various features of the built in debugger (when linked in).
The debug control string is a string of the form
\*(lq\-#<opt1>:<opt2>:...\*(rq, where each option is either
a single flag character or a flag character followed
by a comma separated list.
Available flag characters are: 'd' enable debugging for list
of keywords, 'f' limit debugging to list of function
names, 'F' print source file name, 'L' print source file
line numbers, 'n' print nesting depth, 'o' redirect output
to listed file, 'p' print process name, 't' enable tracing.
.PP
.RS
bru \-ivvv \-#t
.br
bru \-ivvv \-#d:t
.br
bru \-ivvv \-#d,ar_io,verify:F:L
.br
bru \-ivvv \-#d:f,ar_seek
.br
bru \-ivvv \-#d:o,trace.out:t:p
.RE
.PP
Back up the entire root filesystem without
crossing mounted
.RB ( \-m )
filesystem boundaries.
The archive will be written to file
.RB ( \-f )
\*(lq/dev/rmt0\*(rq using an I/O
buffer size
.RB ( \-b )
of 10k bytes.
A record of all files processed will be written to file \*(lqbrulogfile\*(rq
for future reference.
.PP
.RS
cd /
.br
bru \-cvm \-f /dev/rmt0 \-b 10k >brulogfile
.RE
.SH DIAGNOSTICS
.ad
Most diagnostics are reasonably informative. 
The most common have to do with meaningless combinations of options,
incompatible options,
hitting memory or device limits,
unresolved file links,
trying to archive or restore something to which access is normally denied,
or problems with media errors and/or archive corruption.
.SH DEVICE TABLE
.ad
.I Bru\^
contains an internal table of known devices and their characteristics.
This table is dynamically loaded from a data file specified by
the environment variable
.BR BRUTAB\^ ,
or from
.BR /etc/brutab\^ ,
or from an internal default description if neither of the preceding is found.
.SH SIGNAL HANDLING
.ad
.I Bru\^
normally catches both interrupt (SIGINT) and quit (SIGQUIT).
When interrupt is caught during archive creation or extraction,
.I bru\^
completes its work on the current file before cleaning up and exiting.
This is the normal way of aborting
.IR bru .
When a quit signal is caught an immediate exit is taken.
.PP
Note that during file extraction, a quit signal may leave the last file only
partially extracted.
Similarly, a quit signal during archive writing may leave the archive
truncated.
When either interrupt or quit is caught at any other time
an immediate exit is taken.  
.SH ERROR RECOVERY
.ad
When properly configured for a given software/hardware environment,
bru can recover from most common errors.
For example, attempts to use unformatted media are detected, allowing
substitution of formatted media.
Random blocks in an archive can be deliberately overwritten (corrupted)
without affecting
.IR bru 's
ability to recover data from the rest of the archive.
When I/O errors are detected, retries are performed automatically.
Out of order sequencing on multi-volume archive reads is detected,
allowing replacement with the correct volume.
.SH DIRECTORIES
.ad
When creating non-incremental archives
.I bru\^
automatically archives all directories necessary to
fully restore any file from the archive.
During extraction, any required directories which do not
already exist are restored from the archive if possible,
otherwise they are created with appropriate defaults for
the current user.
.PP
The net result is that restoration from incremental
archives (which may not contain all necessary directories),
or incremental restoration from full
archives (which may skip directories needed later),
may result in creation of directories with the default
attributes.
.SH WILDCARDS
.ad
When reading archives
.I bru\^
recognizes file name generation patterns in the same format as
the shell.
This allows greater flexibility in specifying files to be extracted,
compared, or listed.
.PP
Note that the patterns may have to be quoted to prevent expansion by the
shell.
.SH BYTE/WORD SWAPPING
.ad
While reading archives produced on other machines,
.I bru\^
automatically attempts to perform byte and/or word swapping as
necessary.
.SH REMOTE TAPE DRIVES
.ad
On 4.2\ BSD systems, and System V systems that support networking,
.I bru\^
allows the use of remote tape drives for the archive device
(via the
.B \-f
option).
A remote tape drive file name has the form
.RS
.sp
.IR system [. user ]:/dev/???
.sp
.RE
where
.I system
is the remote system,
the optional
.I user
is the login name to use on the remote system if different
from the current user's login name, and
.B /dev/???
is the tape drive to use (1600 BPI or 800 BPI, raw or blocked,
rewinding or non-rewinding, etc.).
In all cases, the user must have the appropriate permissions
on the remote system.
(See also the
.SM
.B CAVEATS
section, below.)
.SH EXIT CODES
.ad
.I Bru\^
always returns meaningful status as follows:
.PP
.RS
0\ \ \ \ Normal exit, no errors or warnings.
.br
1\ \ \ \ Warnings (or interrupted).
.br
2\ \ \ \ Errors (or quit signal).
.RE
.SH SEE ALSO
Under System V, see
.br
.nh
tar(1), cpio(1), volcopy(1M), finc(1M), frec(1M), ff(1M), filesave(1M).
.sp
Under 4.2\ BSD, see
.br
tar(1), dd(1), rsh(1), rmt(8), dump(8), restore(8), rdump(8), rrestore(8).
.SH AUTHOR
Fred Fish
.br
Ported from System V to 4.2\ BSD by Arnold Robbins, at Georgia Tech
.SH UNIX SYSTEM INCOMPATIBILITIES
.hy
.I Bru\^
recognizes special file types that may be allowed on one type of
.B Unix\^
system, but not on another.
For instance, on a 4.2\ BSD system,
.I bru\^
will extract
fifos as plain files (mode 0666, as modified by the
.IR umask\^ ),
and issue an appropriate error message.
Usually,
.I bru\^
will issue two messages.
The first message will be the more descriptive of the two.
.PP
Under System V, when
.I bru\^
sees a symbolic link,
it will check to see if the file to be linked to exists,
and is not a directory.
If so, it will attempt to make a hard link.
If the hard link fails,
.I bru\^
will issue a warning.
.PP
Pyramid corporation conditional symbolic links are also supported.
On a Pyramid, they are restored properly.
On a 4.2\ BSD system,
.I bru\^
will attempt a symbolic link for the file given in the UCB universe.
Under System V,
it will attempt a hard link for the file given in the ATT universe
(with the same constraints as when attempting to restore regular
symbolic links).
.PP
Currently, the only different
.B Unix\^
systems that
.I bru\^
fully understands are System V, 4.2\ BSD, and Pyramid's OSx.
.SH CAVEATS
.ad
Pathnames are limited to 127 characters in length.
This could become a chronic problem under 4.2\ BSD.
.br
.sp
Implementation differences complicate the algorithms for
automatic detection of end of file on devices.
The algorithms can be fooled, hence the 
.B \-s\^
option.
.br
.sp
Special files moved to a machine other than their original host
will generally be useless and possibly even dangerous.
This goes double for symbolic links.
.br
.sp
When extracting files from archives, patterns used to match
directories may result in some unnecessary directories being
extracted.
For example, if the pattern is \*(lqa/*/c\*(rq, and the directory
\*(lqa/b\*(rq is encountered in the archive, the directory file \*(lqa/b\*(rq
will be extracted since it will be needed when (and if)
the file \*(lqa/b/c\*(rq is encountered.
When in doubt, use the
.B \-w\^
option.
.br
.sp
In order to be able to efficiently archive needed directories,
.I bru\^
builds an image of the directory tree for
.I files\^
using dynamically allocated memory.
Since there may be at most 5120 characters passed on the command line,
it is very unlikely that
.I bru\^
will run out of memory while building the tree from command line arguments.
This is not true of file lists read from the standard input,
particularly on machines with limited address space.
.br
.sp
Information about file linkages is also kept in memory.
Some linkages may be lost if memory is exhausted.
.br
.sp
Since
.I bru\^
is owned by root
and runs with \*(lqset\ user\ id\*(rq to allow it to create directories
and special files, it makes every attempt to prevent normal users
from archiving or extracting files they would normally not have access to.
There may be loopholes.
Also note that
anyone with physical or electronic access to an archive,
and knowledge of the archive structure,
can recover
any of its contents by writing their own file extraction program.
.br
.sp
Directories which have filesystems mounted on them will not
be properly archived until the filesystem is unmounted.
This is not generally a problem.
.br
.sp
Explicitly naming both a directory and one of its 
descendents inhibits expansion of the parent directory.
.br
.sp
Explicitly naming a file more than once is ineffective.
.br
.sp
When reading from the raw magnetic tape file (rmtxxx)
.I bru\^
automatically attempts to adjust the I/O buffer size to match
that used to record the archive.
Under certain circumstances it may fail and require help via the
.B \-b\^
option.
.br
.sp
The 4.2\ BSD remote magnetic tape protocol, rmt(8), allows
writing and reading of no more than 10K bytes, so
.BR \-b 10k
must be specified when reading from or writing to a remote tape drive.
Also, using remote tape drives can be slow.
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'generate.c'" '(360 characters)'
if test -f 'generate.c'
then
	echo shar: will not over-write existing file "'generate.c'"
else
cat << \SHAR_EOF > 'generate.c'
/*
 * generate.c
 *
 * produce numbers in a given range for beer.
 */

#include <stdio.h>

main (argc, argv)
int argc;
char **argv;
{
	register int start, end;

	if (argc != 3)
		fprintf (stderr, "usage: %s from to\n", argv[0]), exit (1);

	start = atoi (*++argv);
	end = atoi (*++argv);

	for (; start <= end; start++)
		printf ("%d\n", start);

	exit (0);
}
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'roman.c'" '(1563 characters)'
if test -f 'roman.c'
then
	echo shar: will not over-write existing file "'roman.c'"
else
cat << \SHAR_EOF > 'roman.c'
/*
 * roman.c
 *
 * convert integer to roman numerals for beer
 * most of the code stolen from 'swtfmt'
 */

#include <stdio.h>
#include <ctype.h>

static char *roman ();

main (argc, argv)
int argc;
char **argv;
{
	if (argc != 2)
		fprintf (stderr, "usage: %s integer\n", argv[0]), exit (1);

	printf ("%s\n", roman (atoi (argv[1])));
	exit (0);
}

static char *roman (val)
int val;
{
	static char buf[BUFSIZ];
	register int aval, i;
	register char *cp;

/* this macro is Reiser CPP dependant.... */
#define outchar(ch)	{ if (i < sizeof buf){ buf[i] = 'ch'; i++;}}

	i = 0;
	aval = abs (val);
	if (val < 0)
		outchar (-);

	while (aval >= 1000)
	{
		outchar (m);
		aval -= 1000;
	}
	if (aval >= 900)
	{
		outchar (c);
		outchar (m);
		aval -= 900;
	}
	else if (aval >= 500)
	{
		outchar (d);
		aval -= 500;
	}

	if (aval >= 400)
	{
		outchar (c);
		outchar (d);
		aval -= 400;
	}
	else
		while (aval >= 100)
		{
			outchar (c);
			aval -= 100;
		}
	if (aval >= 90)
	{
		outchar (x);
		outchar (c);
		aval -= 90;
	}
	else if (aval >= 50)
	{
		outchar (l);
		aval -= 50;
	}

	if (aval >= 40)
	{
		outchar (x);
		outchar (l);
		aval -= 40;
	}
	else
		while (aval >= 10)
		{
			outchar (x);
			aval -= 10;
		}

	if (aval >= 9)
	{
		outchar (i);
		outchar (x);
		aval -= 9;
	}
	else if (aval >= 5)
	{
		outchar (v);
		aval -= 5;
	}

	if (aval >= 4)
	{
		outchar (i);
		outchar (v);
		aval -= 4;
	}
	else
		while (aval >= 1)
		{
			outchar (i);
			aval--;
		}

	buf [i] = '\0';

	for (cp = buf; *cp; cp++)
		if (islower (*cp))
			*cp = toupper (*cp);
	return (buf);
}
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'tapesets'" '(353 characters)'
if test -f 'tapesets'
then
	echo shar: will not over-write existing file "'tapesets'"
else
cat << \SHAR_EOF > 'tapesets'
# /etc/beer/tapesets
#
# This file describes how many sets of tapes are available for a
# particular type of backup, and which set to use the next time
# beer is called. While it may edited, beer will recreate it
# each time it is run, automatically updating the numbers.
#
# type		how many	which to use next
#
monthly		4		1

weekly		4		1

daily		2		1

SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'uid.c'" '(68 characters)'
if test -f 'uid.c'
then
	echo shar: will not over-write existing file "'uid.c'"
else
cat << \SHAR_EOF > 'uid.c'
main ()
{
	printf ("uid = %d\teuid = %d\n", getuid(), geteuid());
}
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'verify.c'" '(883 characters)'
if test -f 'verify.c'
then
	echo shar: will not over-write existing file "'verify.c'"
else
cat << \SHAR_EOF > 'verify.c'
/*
 * verify.c
 *
 * make sure beer is being run by the proper person.
 */

#include <stdio.h>
#include <pwd.h>

main (argc, argv)
int argc;
char **argv;
{
	struct passwd *ent, *getpwent ();

	if (argc != 2)
	{
		fprintf (stderr, "usage: %s login_name\n", argv[0]);
		exit (1);
	}

	if (getuid () == 0)		/* root can do what he wants */
		exit (0);
	else if (geteuid () != 0)
	{
		fprintf (stderr, "%s: not running setuid to root.\n", argv[0]);
		exit (2);
	}

	if ((ent = getpwent()) == NULL)
	{
		fprintf (stderr, "%s: can't read /etc/passwd.\n", argv[0]);
		exit (3);
	}
	else
	{
		do
		{
			if (strcmp (ent -> pw_name, argv[1]) == 0)
			{
				endpwent();
				if (getuid() == ent -> pw_uid)
					exit (0);
				else
					exit (4);
			}
		}
		while ((ent = getpwent()) != NULL);
	}

	endpwent();
	fprintf (stderr, "%s: '%s' is not in /etc/passwd.\n", argv[0], argv[1]);
	exit (4);
}
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0
-- 
Arnold Robbins
CSNET:	arnold@gatech	ARPA:	arnold%gatech.csnet@csnet-relay.arpa
UUCP:	{ akgua, allegra, hplabs, ihnp4, seismo, ut-sally }!gatech!arnold

Real Unix hackers disdain wimpy languages like C and AWK in favor of /bin/sh.