[mod.sources] mdump - multiple dump per tape utility

sources-request@genrad.UUCP (08/08/85)

Mod.sources:  Volume 2, Issue 36
Submitted by: ihnp4!uiucdcs!uiucuxc!paul (Paul Pomes)

Mdump and st were written to ease the pain of UNIX backups for our
operations staff.  It's compressed the clock time needed to run a
daily set of backups by almost two thirds.  The time savings are due
to eliminating most of the tape mounts and waiting for tape rewinds.
Listings are generated automatically though they can be suppressed.
It's been in use here for about two months in its present form.
VAX backups are done on local drives, the Pyramid 90x goes over an
Ethernet to the VAX drives.  Most of the error recovery code was
added because mt commands over the network would sometimes mysteriously
disappear.  I've thought about alarm() timeouts but haven't come up
with a reasonable implementation that wouldn't blow long rewinds out
of the water by accident.  There's also the fact that it's working
reasonably well and six other projects are on the fire.  Anyway I
think this will be extremely useful for some sites, handy at others,
and pretty window dressing at the rest.

         Paul Pomes

UUCP:	 {ihnp4,pur-ee,convex}!uiucdcs!uiucuxc!paul
ARPANET: paul%uiucuxc@uiuc.arpa
CSNET:	 paul%uiucuxc@uiuc.csnet
ICBM:	 40 07 N / 88 13 W
US Mail: Univ of Illinois, CSO, 1304 W Springfield Ave, Urbana, IL  61801

#!/bin/sh
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	README
#	Makefile
#	dumptab.5
#	dumptab.c
#	dumptab.h
#	etc.dumptab
#	getdtent.3
#	mdump.8
#	mdump.c
#	st.8
#	st.c
sed 's/^X//' << 'SHAR_EOF' > README
XMdump and st together provide a coherent method for organizing multiple
Xinstances of dump(8) onto a single magnetic tape volume.
X
XSeveral assumptions have been made: 1) filesystems are backed up on a
Xdaily basis capturing changes made in the last 24 hours or since the
Xlast weekly.  Weekly tapes capture all changes made since the last 
Xlevel 0 dump.
X
XTo install, edit the #defines's in mdump.c to properly reflect the
Xdesired commands.  The dump command issued via a system() call is
Xbuilt in the wrt_dmp() procedure.
X
XCopy dumptab.h to /usr/include.
X
XEdit etc.dumptab to reflect the order desired in dumping filesystems
Xand the day-of-week for the weekly dump.  I recommend putting the
Xsmaller filesystems first on the tape.  Should the tape volume overflow,
Xa smaller number of individual tapes will be needed to dump the
Xremaining filesystems.
X
XCopy etc.dumptab to /etc/dumptab.
X
XRun "make", then "make install".
X
X         Paul Pomes
X
XUUCP:	 {ihnp4,pur-ee,convex}!uiucdcs!uiucuxc!paul
XARPANET: paul%uiucuxc@uiuc.arpa
XCSNET:	 paul%uiucuxc@uiuc.csnet
XICBM:	 40 07 N / 88 13 W
XUS Mail: Univ of Illinois, CSO, 1304 W Springfield Ave, Urbana, IL  61801
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > Makefile
X# Makefile for mdump, a utility for putting multiple dump files onto a
X# single tape volume.
X#
X# Written by Paul Pomes, University of Illinois, Computing Services Office
X# Copyright 1985 by Paul Pomes and University of Illinois Board of Trustees
X#
X# This program may be freely reproduced and distributed provided the 
X# copyright above is included in all copies.  It may not be included
X# in any software product or distribution sold for profit without the
X# permission of the author.
X#
X# UUCP:		{ihnp4,pur-ee,convex}!uiucdcs!uiucuxc!paul
X# ARPANET:	paul%uiucuxc@uiuc.arpa
X# CSNET:	paul%uiucuxc@uiuc.csnet
X# US Mail:	Univ of Illinois, CSO, 1304 W Springfield Ave, Urbana, IL  61801
X#
X# $Header: Makefile,v 1.1 85/07/02 13:47:16 root Exp $
X
XCFLAGS=	-O
XLDFLAGS=
X
Xall:	mdump st
X
Xmdump:	mdump.o dumptab.o
X	cc mdump.o dumptab.o -o mdump ${LDFLAGS}
X
Xst:	st.o dumptab.o
X	cc st.o dumptab.o -o st
X
Xclean:
X	rm -f *.o mdump st make.log lint.out a.out
X
Xlint:
X	lint -habcx mdump.c dumptab.c
X	lint -habcx st.c dumptab.c
X
Xinstall: mdump st
X	install -s mdump /etc
X	install -s st /etc
X	cp mdump.8 /usr/man/manl/mdump.l
X	cp dumptab.5 /usr/man/manl/dumptab.l
X	cp getdtent.3 /usr/man/manl/getdtent.l
X	cp st.8 /usr/man/manl/st.l
X
Xdumptab.o:	dumptab.c dumptab.h
X	cc ${CFLAGS} -c dumptab.c
X
Xmdump.o:	mdump.c dumptab.h
X	cc ${CFLAGS} -c mdump.c
X
Xst.o:	st.c dumptab.h
X	cc ${CFLAGS} -c st.c
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > dumptab.5
X.TH DUMPTAB 5 "UofI CSO"
X.SH NAME
Xdumptab \- multiple dump per tape description file
X.SH DESCRIPTION
X.I Dumptab
Xcontains for each filesystem the
Xfollowing information:
X.HP 10
Xname (filesystem name, contains no upper case)
X.br
X.ns
X.HP 10
Xtape file position (1 to n)
X.br
X.ns
X.HP 10
Xday of weekly backup (Sunday = 0)
X.br
X.ns
Xdescriptive comment
X.PP
XThis is an ASCII file.  Each field within each filesystem's entry
Xis separated from the next by a colon.
XEach filesystem is separated from the next by a new-line.
X.PP
X.I Dumptab
Xis used by
X.IR mdump (8)
Xand
X.IR st (8)
Xto define the order and backup schedule of filesystems.
X.SH FILES
X/etc/dumptab
X.SH "SEE ALSO"
Xgetdtent(3), mdump(8), st(8)
X.SH BUGS
XIt's not clear this is the best approach, however it was the fastest to
Ximplement.
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > dumptab.c
X/*
X * dumptab -- read the dumptab file
X *
X * Dumptab provides a set of routines similar in spirit to getpwent(3).
X * getdtent() returns a pointer to a dumptab entry, getdtnam(name) returns
X * a dumptab entry pointer for the named filesystem, getdtpos(position)
X * returns a dumptab entry pointer for the filesystem at the indicated
X * tape position, setdtent opens the dumptab file, and enddtent closes it.
X *
X * Written by Paul Pomes, University of Illinois, Computing Services Office
X * Copyright 1985 by Paul Pomes and University of Illinois Board of Trustees
X *
X * This program may be freely reproduced and distributed provided the
X * copyright above is included in all copies.  It may not be included
X * in any software product or distribution sold for profit without the
X * permission of the author.
X *
X * UUCP:	{ihnp4,pur-ee,convex}!uiucdcs!uiucuxc!paul
X * ARPANET:	paul%uiucuxc@uiuc.arpa
X * CSNET:	paul%uiucuxc@uiuc.csnet
X * US Mail:	Univ of Illinois, CSO, 1304 W Springfield Ave, Urbana, IL  61801
X *
X * $Log:	dumptab.c,v $
X * Revision 1.1  85/07/02  13:47:31  root
X * Initial revision
X * 
X */
X
X#ifndef lint
Xstatic char	RcsId[] = "$Header: dumptab.c,v 1.1 85/07/02 13:47:31 root Exp $";
X#endif
X
X#include	<stdio.h>
X#include	<dumptab.h>
X
Xstatic char *DUMPTAB = "/etc/dumptab";
Xstatic FILE *dtf = NULL;
Xstatic char line[BUFSIZ+1];
Xstatic struct dumptab dumptab;
X
Xsetdtent()
X{
X	if (dtf == NULL)
X		dtf = fopen(DUMPTAB, "r");
X	else
X		rewind(dtf);
X}
X
Xenddtent()
X{
X	if (dtf != NULL) {
X		(void) fclose(dtf);
X		dtf = NULL;
X	}
X}
X
Xstatic char *
Xdtskip(p)
Xregister char *p;
X{
X	while (*p && *p != ':' && *p != '\n')
X		p++;
X	if (*p)
X		*p++ = 0;
X	return(p);
X}
X
Xstruct dumptab *
Xgetdtent()
X{
X	register char *p;
X
X	if (dtf == NULL) {
X		if ((dtf = fopen( DUMPTAB, "r" )) == NULL)
X			return(0);
X	}
X	p = fgets(line, BUFSIZ, dtf);
X	if (p == NULL)
X		return(0);
X	dumptab.dt_name = p;
X	p = dtskip(p);
X	dumptab.dt_position = atoi(p);
X	p = dtskip(p);
X	dumptab.dt_weekly = atoi(p);
X	p = dtskip(p);
X	dumptab.dt_comment = p;
X	while (*p && *p != '\n')
X		p++;
X	*p = '\0';
X	return(&dumptab);
X}
X
Xstruct dumptab *
Xgetdtpos(num)
Xregister num;
X{
X	register struct dumptab *p;
X
X	setdtent();
X	while ((p = getdtent()) && p->dt_position != num)
X		;
X	enddtent();
X	return(p);
X}
X
Xsetdtfile(file)
Xchar *file;
X{
X	DUMPTAB = file;
X}
X
Xstruct dumptab *
Xgetdtnam(name)
Xchar *name;
X{
X	register struct dumptab *p;
X
X	setdtent();
X	while ((p = getdtent()) && strcmp(name, p->dt_name))
X		;
X	enddtent();
X	return(p);
X}
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > dumptab.h
X/* $Header: dumptab.h,v 1.1 85/07/02 13:48:41 root Exp $ */
X
Xstruct	dumptab {
X	char	*dt_name;
X	int	dt_position;	/* 1 to n */
X	int	dt_weekly;	/* Sunday = 0, Saturday = 6 */
X	char	*dt_comment;	/* other information */
X};
X
Xstruct dumptab *getdtent(), *getdtnam(), *getdtpos();
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > etc.dumptab
X/etc/dumptab:0:0:This file must be at position 0 as tape catalogue
X/:1:2:The root filesystem, weekly dumped on Tuesday
X/mnt:2:2:
X/pub:3:5:
X/cerlsys:4:1:
X/cerl:5:3:
X/acct:6:4:acct weekly dumped on Thursday, uses a new tape every month
X/misc:7:4:
X/srl:8:4:
X/usr/spool:9:2:
X/usr/src:10:3:
X/irm:11:5:
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > getdtent.3
X.TH GETTDENT 3  "UofI CSO"
X.SH NAME
Xgetdtent, getdtnam, setdtent, enddtent \- get dumptab file entry
X.SH SYNOPSIS
X.nf
X.B #include <dumptab.h>
X.PP
X.B struct passwd *getdtent()
X.PP
X.B struct passwd *getdtnam(name)
X.B char *name;
X.PP
X.B struct passwd *getdtpos(position)
X.B int position;
X.PP
X.B int setdtent()
X.PP
X.B int enddtent()
X.fi
X.SH DESCRIPTION
X.IR Getdtent ,
X.IR getdtnam ,
Xand
X.I getdtpos
Xeach return a pointer to an object with the following structure
Xcontaining the broken-out fields of a line in the dumptab file.
X.RS
X.PP
X.nf
X.so /usr/include/dumptab.h
X.ft R
X.ad
X.fi
X.RE
X.PP
X.I Getdtent
Xreads the next line (opening the file if necessary);
X.I setdtent
Xrewinds the file;
X.I enddtent
Xcloses it.
X.PP
X.I Getdtnam
Xsearches from the beginning until a matching
X.I name
Xis found (or until EOF is encountered).
X.PP
X.I Getdtpos
Xsearches from the beginning until a matching
X.I position
X(file number on a tape volume) is found (or until EOF is encountered).
X.SH FILES
X/etc/dumptab
X.SH "SEE ALSO"
Xdumptab(5)
X.SH DIAGNOSTICS
XNull pointer
X(0) returned on EOF or error.
X.SH BUGS
XAll information is contained in a static area so it must be copied if it is
Xto be saved.
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > mdump.8
X.TH MDUMP 8 "UofI CSO"
X.SH NAME
Xmdump \- multiple dump per tape utility
X.SH SYNOPSIS
X.B mdump
X[
X.B \-w
X] [
X.B \-d
X] [
X.B \-l
X] [
X.B \-e
X] [
X\fB\-t \fIday-of-week\fR
X] [
X\fB\-f \fI[host:]/dev/rmtx\fR
X]
X.SH DESCRIPTION
X.I Mdump
Xsimplifies the backup procedure for systems with multiple filesystems
Xby putting all daily and/or weekly backups onto a single tape.
X.PP
XThe order and selection of filesystems to be dumped is determined by
X.I /etc/dumptab 
Xand by the options
X.B \-w
Xand
X.BR \-d .
X.B \-w
Xselects filesystems scheduled for weekly backup today while
X.B \-d
Xselects filesystems scheduled for daily backup.
XSpecifying both switches causes daily and weekly backups to be selected.
XA null file is inserted on the tape for non-selected filesystems
Xto preserve file ordering.
XThe
X.IR st (8)
Xcommand (set tape) will position the backup tape to the correct tape
Xfile for restores.
X.PP
XThe backup device is selected with the
X.B \-f
Xoption.
XRemote backup over the network is accomplished by specifying the device
Xname in the same manner as
X.IR rdump (8),
Xe.g., uiucuxc:/dev/rmt0.
X.PP
XAfter
X.I mdump
Xhas finished writing the dump files,
Xa listing of each dump will be created and sent to the \s-1IBM\s0 printers
Xunless the
X.B \-l
Xoption is given to suppress the listings.
X.PP
XIn the case of positioning errors during the listing phase of
X.IR mdump ,
Xhanging rsh commands are the usual cause,
X.B \-e
Xwill execute the listing phase of
X.I mdump
Xonly.
XAt least one of the flags
X.B \-d
Xand/or
X.B \-w
Xmust be specified in addition to the
X.B \-e
Xflag.
X.PP
XTo run
X.I mdump
Xas though it were a different day of the week, e.g.,
Xcreate or list a Monday tape on Wednesday, the 
X\fB\-t \fIday-of-week\fR
Xflag is supplied where
X.I day-of-week
Xis one of mon, tue, wed, etc.
X.SH NOTES
XIn the case where
X.I mdump
Xfound no files to dump,
Xthe subsequent listing phase will complain that
Xit "Cannot find file dump list".
X.I Mdump
Xwill print a message saying that the list command exitted abnormally
Xand ask whether to continue.
XEntering 'y' directs
X.I mdump
Xto continue listing the other filesystems.
XAny other response causes an exit.
X.SH FILES
X/etc/dumptab, /etc/dump
X.SH "SEE ALSO"
Xdumptab(5), dump(8), rdump(8), st(8)
X.SH BUGS
XIt's not clear this is the best approach, however it was the fastest to
Ximplement.
X.PP
XNo provision is made for tape overflow.  This is an incentive to use 6250
Xbpi drives.
X.PP
XAny tape error means restarting
X.I mdump
Xfrom the beginning.
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > mdump.c
X/*
X * mdump -- put daily/weekly backups onto a single tape
X *
X * Mdump simplifies the tedious process of daily and weekly backups by
X * putting all daily backups onto a single tape.  Weekly backups are
X * performed the same way.  The /etc/dumptab file defines the file
X * position on the tape each filesystem is written at and the day of
X * the week the weekly backup is done.  Filesystems that are scheduled
X * for a weekly backup are marked with an empty file on the daily tape
X * so that file numbering remains constant.
X *
X * usage: mdump [-d] [-w] [-l] [-e] [-t day] [-f [host:]/dev/rmtx] [-db]
X *
X * -d	write a daily backup tape
X *
X * -w	write a weekly backup tape
X *
X * -l	suppress generation of the listing file
X *
X * -e	run listing code after an error exit
X *
X * -t day	run mdump for day (sun - sat) instead of today
X *
X * -f [host:]/dev/rmtx	tape device name.  If the host: prefix
X *	is used, the backup tape will be written over the network to
X *	the tape drive on the named host.
X *
X * -db	Write debug information to stderr
X *
X * Written by Paul Pomes, University of Illinois, Computing Services Office.
X * Copyright 1985 by Paul Pomes and University of Illinois Board of Trustees
X *
X * This program may be freely reproduced and distributed provided the
X * copyright notice above is included in all copies.  It may not be
X * included in any software product or distribution sold for profit
X * without the permission of the author.
X *
X * UUCP:	{ihnp4,pur-ee,convex}!uiucdcs!uiucuxc!paul
X * ARPANET:	paul%uiucuxc@uiuc.arpa
X * CSNET:	paul%uiucuxc@uiuc.csnet
X * ICBM:	40 07 N / 88 13 W
X * US Mail:	Univ of Illinois, CSO, 1304 W Springfield Ave, Urbana, IL  61801
X *
X * $Log:	mdump.c,v $
X * Revision 1.7  85/08/07  16:30:08  paul
X * Added bare-bones System 5 defines (index -> strchr, etc).  Large hacks
X * needed to do the same for the mag tape ioctls.  Another day, far away.
X * 
X * Revision 1.6  85/08/07  16:03:57  root
X * Added n switch to dump command so that operators are notified.
X * Extremely effective annoyance feature.   -pbp
X * 
X * Revision 1.5  85/07/11  14:44:07  paul
X * Added recovery question for restore listings of filesystems with no files.
X * pbp
X * 
X * Revision 1.4  85/07/09  21:18:09  root
X * *** empty log message ***
X * 
X * Revision 1.3  85/07/05  17:13:39  root
X * Added -t day-of-week flag for enhanced error recovery.   -pbp
X * 
X * Revision 1.2  85/07/03  23:23:04  root
X * Added -e flag to force listings after "rsh mt" errors.   -pbp
X * 
X * Revision 1.1  85/07/02  13:47:46  root
X * Initial revision
X */
X
X#ifndef lint
Xstatic char	RcsId[] = "$Header: mdump.c,v 1.7 85/08/07 16:30:08 paul Exp $";
X#endif
X
X#include	<stdio.h>
X#ifdef	SYS5
X#include	<string.h>
X#define		index		strchr
X#define		rindex		strrchr
X#else
X#include	<strings.h>
X#endif
X#include	<ctype.h>
X#include	<sys/time.h>
X#include	<sys/file.h>
X#include	<sys/types.h>
X#include	<sys/mtio.h>
X#include	<sys/ioctl.h>
X#include	<dumptab.h>
X
X#define		equal(s1, s2)	(strcmp(s1, s2) == 0)
X
X/* default output devices */
X#define		DEF_DEV		"/dev/rmt0"
X#define		DEF_NRDEV	"/dev/nrmt0"
X
X/* dumptab file.  must be the same as referenced by getdtent() functions */
X#define		DUMPTAB		"/etc/dumptab"
X
X/* temp file for listings */
X#define		DLIST		"/tmp/mdumpXXXXXX"
X
X/* maximum number of filesystems */
X#define		MAX_FS		30
X
X/* size of shorter strings, e.g., host[] */
X#define		SSTR_SIZE	12
X
X/* size of longer strings, e.g., command[] */
X#define		LSTR_SIZE	160
X
X/* command for generating and formatting listings.
X * arguments in order are: host, ':' (if host defined, ' ' otherwise),
X * nrdevice, myname, dt->dt_name, list_file.  don't delete the leading
X * blank as a remote restore replaces that with an 'r'.
X */
X#define		LST_CMD		" restore tf %s%c%s | sort +1 | pr -3 -w132 -f -h \"%s Dump Listing of %s\" >> %s"
X
X/* listing file print command.  arguments are myname and list_file */
X#define		PRT_CMD		"ibmprint -f -h \"%s Dlist\" %s"   /* vax */
X/* #define		PRT_CMD		"lpr -r -s -J %s_Dlist %s" /* pyramid */
X
X/* command to copy the /etc/dumptab file to the tape device.  arguments
X * to DD_CMD are DUMPTAB and nrdevice.  for the remote command an rcp and
X * rsh command are used.  arguments to RDD_CMD are DUMPTAB, host, host,
X * nrdevice.
X */
X#define		DD_CMD		"dd if=%s of=%s bs=512"
X#define		RDD_CMD		"rcp %s %s:/tmp/xyzzy.mdump; rsh %s \"dd if=/tmp/xyzzy.mdump of=%s bs=512; rm /tmp/xyzzy.mdump\""
X
X/* copy of argv[0] for error messages */
Xchar		*self;
X
X/* resident host name */
Xchar		myname[SSTR_SIZE];
X
X/* debug messages printed if set */
Xint		debug;
X
X/* run listing code only */
Xint		elist;
X
X/* state table of tape files.  if dump_state[0] is set, then there's a dump
X * file there.  If zero, there's a tape mark on the tape instead.  If -1
X * then EOT has been reached or some other dump(8) error.  Typical usage
X * would be dump_state[dt->dt_position].
X */
Xint		dump_state[MAX_FS];
X
X/* mag tape stuff */
Xstruct mt_cmds {
X	char	*c_name;
X	int	c_code;
X	int	c_ronly;
X} com[] = {
X	{ "eof",	MTWEOF,	0 },
X	{ "fsf",	MTFSF,	1 },
X	{ "bsf",	MTBSF,	1 },
X	{ "rew",	MTREW,	1 },
X	{ 0 }
X};
X
Xextern time_t	time();
X
Xmain(argc, argv)
Xint	argc;
Xchar	**argv;
X{
X	/* a useful counter */
X	int		i;
X
X	/* weekly or daily backups (can be both) */
X	int		weekly, daily;
X
X	/* create a compact listing if set */
X	int		dlist;
X
X	/* run mdump for day argument instead of today of set */
X	char		newday[4];
X
X	/* listing file */
X	char		*list_file = DLIST;
X
X	/* backup device */
X	char		*device, *nrdevice;
X
X	/* host machine */
X	char		host[SSTR_SIZE];
X
X	/* time stuff */
X	struct tm	*tm;
X	time_t		clock;
X
X	/* dumptab info */
X	struct dumptab	*dt;
X
X	/* library routines */
X	char		*malloc(), *mktemp();
X
X	/* routine to set new tm->tm_wday value for -t argument */
X	struct tm	*setday();
X
X	/* squirrel a copy of *argv[0] away for use in error messages */
X	self = malloc((unsigned) (strlen(*argv) + 1));
X	(void) strcpy(self, *argv);
X
X	dlist = 1;
X	weekly = daily = 0;
X	newday[0] = host[0] = '\0';
X	device = DEF_DEV;
X	nrdevice = DEF_NRDEV;
X	(void) gethostname(myname, SSTR_SIZE-1);
X	/*
X	 * parse arguments
X	 */
X	i = 1;
X	while (i < argc && *argv[i] == '-') {
X		if (equal(argv[i]+1, "db")) {
X			/* db - set debug level */
X			debug++;
X			i++;
X			fprintf(stderr, "%s: debug option enabled\n", self);
X		}
X		else if (equal(argv[i]+1, "d")) {
X			/* d - daily backups */
X			daily++;
X			i++;
X		}
X		else if (equal(argv[i]+1, "w")) {
X			/* w - weekly backups */
X			weekly++;
X			i++;
X		}
X		else if (equal(argv[i]+1, "l")) {
X			/* l - suppress listings of files */
X			dlist = 0;
X			i++;
X		}
X		else if (equal(argv[i]+1, "e")) {
X			/* e - run listing code only */
X			printf("Listings only - nothing will be written to tape\n");
X			elist++;
X			i++;
X		}
X		else if (equal(argv[i]+1, "t")) {
X			/* t - read new dump day-of-week from next argument */
X			i++;
X			(void) strncpy(newday, argv[i], 3);
X			i++;
X		}
X		else if (equal(argv[i]+1, "f"))
X		{
X			/*
X			 * f - read backup device from next argument
X			 * turn /dev/rmt names into /dev/nrmt
X			 */
X			char	temp[40], *p1, *p2;
X
X			i++;
X			p2 = (char *) NULL;
X			if ((p1 = index(argv[i], ':')) != 0) {
X				(void) strncpy(host, argv[i], p1-argv[i]+1);
X				*(index(host, ':')) = '\0';
X				p1++;
X			}
X			if (p1 == 0)
X				p1 = argv[i];
X			device = malloc((unsigned) (strlen(p1) + 1));
X			(void) strcpy(device, p1);
X			if ((p2 = rindex(argv[i], '/')) == 0) {
X				fprintf(stderr, "%s: Output device must be a tape drive, e.g., /dev/rmt1, uxc:/dev/rmt2\n", self);
X				exit(1);
X			}
X			p2++;
X			(void) strncpy(temp, p1, p2-p1);
X			*(temp + (p2-p1)) = '\0';
X			nrdevice = malloc((unsigned) (strlen(argv[i])+2));
X			if (*p2 == 'n')
X				(void) sprintf(nrdevice, "%s%s", temp, p2);
X			else
X				(void) sprintf(nrdevice, "%sn%s", temp, p2);
X			if (debug)
X				fprintf(stderr, "host %s, device %s, nrdevice %s\n", host, device, nrdevice);
X			i++;
X		}
X		else
X		{
X			/* command line errors */
X			fprintf(stderr, "%s: %s - bad flag\n", self, argv[i]+1);
X			fprintf(stderr, "Usage: %s [-d] [-w] [-e] [-l] [-f [host:]/dev/rmtx] [-db]\n", self);
X			exit(1);
X		}
X	}
X	if (! (weekly || daily)) {
X		fprintf(stderr, "%s: No action specified.\n\tInvoke %s with at least one of the following: -w, -d\n", self, self);
X		exit(1);
X	}
X	clock = time((time_t *) 0);
X	if (*newday)
X		tm = setday(newday);
X	else
X		tm = localtime(&clock);
X	mtio(host, device, "rew");
X	wrt_dtinfo(host, nrdevice);
X	setdtent();
X	for (i = 1; (dt = getdtpos(i)); i++) {
X		if (dt->dt_weekly == tm->tm_wday) {
X			if (weekly) {
X				if (wrt_dmp(host, nrdevice, dt, 'w'))
X					break;
X			}
X			else
X				wrt_dummy(host, nrdevice, dt);
X		}
X		else {
X			if (daily) {
X				if (wrt_dmp(host, nrdevice, dt, 'd'))
X					break;
X			}
X			else
X				wrt_dummy(host, nrdevice, dt);
X		}
X	}
X	/* write the last EOF to form EOT and rewind the tape
X	 * by using the rewind-on-close device.
X	 */
X	if (! elist) {
X		printf("Really rewinding tape\n");
X		mtio(host, device, "eof");
X	}
X	if (dlist) {
X		char	command[LSTR_SIZE];
X
X		(void) mktemp(list_file);
X		mtio(host, device, "rew");
X
X		/* skip over the zero'th file of /etc/dumptab */
X		mtio(host, nrdevice, "fsf");
X		setdtent();
X		for (i = 1; (dt = getdtpos(i)); i++)
X			if (list_tape(host, nrdevice, dt, list_file))
X				break;
X		(void) sprintf(command, PRT_CMD, myname, list_file);
X		if (debug)
X			fprintf(stderr, "print command: %s\n", command);
X		if (i = (system(command) >> 8))
X			fprintf(stderr, "%s: %s exitted abnormally (%d).\n", self, command, i);
X		printf("Last rewind of tape\n");
X		mtio(host, device, "rew");
X	}
X	exit(0);
X}
X
X/*
X * mtio -- perform a local/remote tape operation
X *
X *	for local tapes (host[0] == '\0') use the ioctl calls.
X *	remote tapes use an rsh command.
X *
X *	parameters:
X *		host (IN) -- name of remote host, if host[0] != '\0'
X *		dev (IN) -- tape drive name
X *		cp (IN) -- tape opcode
X *	returns:
X *		none
X *	side effects:
X *		none
X *	deficiencies:
X *		any error causes an error exit from the program
X */
Xmtio(host, dev, cp)
Xchar	*host, *dev, *cp;
X{
X	struct mt_cmds	*comp;
X
X	/* tape file descriptor */
X	int		mtfd;
X
X	/* return code from system() call */
X	int		status;
X
X	/* from sys/mtio.h */
X	struct mtop	mt_com;
X
X	/* buffer for rsh command */
X	char		command[LSTR_SIZE];
X
X	if (*host == '\0') {
X		for (comp = com; comp->c_name != NULL; comp++)
X			if (strncmp(cp, comp->c_name, strlen(cp)) == 0)
X				break;
X		if (comp->c_name == NULL) {
X			fprintf(stderr, "%s: mtio: don't grok \"%s\"\n", self, cp);
X			exit(1);
X		}
X		if ((mtfd = open(dev, comp->c_ronly ? 0 : 2)) < 0) {
X			fprintf(stderr, "%s: mtio: open of ", self);
X			perror(dev);
X			exit(1);
X		}
X		mt_com.mt_op = comp->c_code;
X		mt_com.mt_count = 1;
X		if (ioctl(mtfd, MTIOCTOP, &mt_com) < 0) {
X			fprintf(stderr, "%s: mtio: %s %s %d ", self, dev,
X				comp->c_name, mt_com.mt_count);
X			perror("failed");
X			exit(1);
X		}
X		(void) close(mtfd);
X	}
X	else {
X		(void) strcpy(command, "rsh ");
X		(void) strcat(command, host);
X		(void) strcat(command, " mt -t ");
X		(void) strcat(command, dev);
X		(void) strcat(command, " ");
X		(void) strcat(command, cp);
X		if (debug)
X			fprintf(stderr, "tape command %s\n", command);
X		if (status = (system(command) >> 8)) {
X			fprintf(stderr, "%s: %s exitted abnormally (%d).  Restart %s\n", self, command, status, self);
X			exit(1);
X		}
X	}
X}
X
X/*
X * wrt_dmp -- write a filesystem dump on the dump tape
X *
X *	for local tapes (host[0] == '\0') use dump
X *	remote tapes use rdump
X *
X *	parameters:
X *		host (IN) -- name of remote host, if host[0] != '\0'
X *		dev (IN) -- tape drive name
X *		dt (IN) -- dumptab pointer
X *		type (IN) -- weekly or daily dump
X *	returns:
X *		0 if dump command successful, 1 if not
X *	side effects:
X *		alters dump_state table
X *	deficiencies:
X *		any error causes an error exit from the program
X */
Xwrt_dmp(host, dev, dt, type)
Xchar		*host, *dev, type;
Xstruct dumptab	*dt;
X{
X	char	command[LSTR_SIZE];
X	int	status;
X
X	if (elist) {
X		dump_state[dt->dt_position] = 1;
X		return(0);
X	}
X	(void) strcpy(command, " dump nu");
X	switch (type) {
X	    case 'd':
X		(void) strcat(command, "5");
X		break;
X	    case 'w':
X		(void) strcat(command, "4");
X		break;
X	    default:
X		fprintf(stderr, "%s: unknown dump type %c\n", self, type);
X		exit(1);
X	}
X	(void) strcat(command, "f ");
X	if (*host != '\0') {
X		command[0] = 'r';
X		(void) strcat(command, host);
X		(void) strcat(command, ":");
X	}
X	(void) strcat(command, dev);
X	(void) strcat(command, " ");
X	(void) strcat(command, dt->dt_name);
X	if (debug)
X		fprintf(stderr, "dump command %s\n", command);
X	printf("Dumping %s\n", dt->dt_name);
X
X	/* dump(8) exits with 1 if all is well */
X	if ((status = (system(command) >> 8)) != 1) {
X		fprintf(stderr, "%s: %s exitted abnormally (%d).\n", self, command, status);
X		fprintf(stderr, "\tTape will rewind and create listings of the successful dumps.\n");
X		dump_state[dt->dt_position] = -1;
X		mtio(host, dev, "bsf");
X		return(1);
X	}
X	dump_state[dt->dt_position] = 1;
X	return(0);
X}
X
X/*
X * list_tape -- generate a "restore tf" listing for the current tape file
X *
X *	list_tape creates a command line to be executed by system() and
X *	re-directs the output to a listing file.
X *
X *	parameters:
X *		host (IN) -- name of remote host, if host[0] != '\0'
X *		dev (IN) -- tape drive name
X *		dt (IN) -- dumptab pointer
X *		lfile (IN) -- name of listing file
X *	returns:
X *		0 for normal results, 1 if at end of tape
X *	side effects:
X *		none
X *	deficiencies:
X *		any error causes an error exit from the program
X */
Xlist_tape(host, dev, dt, lfile)
Xchar		*host, *dev, *lfile;
Xstruct dumptab	*dt;
X{
X	FILE		*fp;
X	int		status;
X	char		command[LSTR_SIZE];
X
X	if (dump_state[dt->dt_position] == 0) {
X		/* no file here so write a message on the dump listing */
X		if ((fp = fopen(lfile, "a")) == NULL) {
X			fprintf(stderr, "%s: list_tape ", self);
X			perror(lfile);
X			exit(1);
X		}
X		fprintf(fp, "\n\n\tNo dump file for %s on this tape.\n\f",
X			dt->dt_name);
X		(void) fclose(fp);
X		/* advance the tape by one tape mark */
X		printf("Skipping dummy file for %s\n", dt->dt_name);
X		mtio(host, dev, "fsf");
X		return(0);
X	}
X	else if (dump_state[dt->dt_position] == 1) {
X		/* build the appropriate restore tf command and execute it */
X		(void) sprintf(command, LST_CMD, host, (*host ? ':' : ' '),
X			dev, myname, dt->dt_name, lfile);
X		if (*host != '\0')
X			command[0] = 'r';
X		if (debug)
X			fprintf(stderr, "list command %s\n", command);
X		printf("Listing %s\n", dt->dt_name);
X		if (status = (system(command) >> 8)) {
X			char	c,d;
X
X			fprintf(stderr, "%s: %s exitted abnormally (%d).  Continue? [yn] ", self, command, status);
X			while ((d = getc(stdin)) != '\n')
X				c = d;
X			if (c != 'y')
X				exit(1);
X		}
X		/* advance the tape to end of file */
X		mtio(host, dev, "fsf");
X		return(0);
X	}
X	return(1);
X}
X
X/*
X * wrt_dtinfo -- write /etc/dumptab onto the tape
X *
X *	writing a copy of /etc/dumptab onto the tape as file #0 provides
X *	a catalogue of the tape files and insulates backup volumes from
X *	changes in /etc/dumptab.  Elsewhere on the tape it acts as a 
X *	place holder for a non-selected dump, e.g., daily on a weekly-only
X *	tape.
X *
X *	parameters:
X *		host (IN) -- name of remote host
X *		dev (IN) -- tape drive name
X *	returns:
X *		none
X *	side effects:
X *		none
X *	deficiencies:
X *		any error causes an error exit from the program
X */
Xwrt_dtinfo(host, dev)
Xchar	*host, *dev;
X{
X	char	command[LSTR_SIZE];
X	int	status;
X
X	if (elist)
X		return;
X	if (*host)
X		(void) sprintf(command, RDD_CMD, DUMPTAB, host, host, dev);
X	else
X		(void) sprintf(command, DD_CMD, DUMPTAB, dev);
X	printf("Copying %s to tape\n", DUMPTAB);
X	if (status = (system(command) >> 8)) {
X		fprintf(stderr, "%s: %s exitted abnormally (%d).  Restart %s\n", self, command, status, self);
X		exit(1);
X	}
X}
X
X/*
X * wrt_dummy -- write a dummy file onto the tape
X *
X *	write a dummy file onto the tape to take the place of a non-
X *	scheduled backup.  this preserves tape order.
X *
X *	parameters:
X *		host (IN) -- name of remote host
X *		dev (IN) -- tape drive name
X *		dt (IN) -- dumptab pointer
X *	returns:
X *		none
X *	side effects:
X *		alters dump_state table
X *	deficiencies:
X *		any error causes an error exit from the program
X */
Xwrt_dummy(host, dev, dt)
Xchar		*host, *dev;
Xstruct dumptab	*dt;
X{
X	char	command[LSTR_SIZE];
X	int	status;
X
X	if (elist)
X		return;
X	if (*host)
X		(void) sprintf(command, RDD_CMD, DUMPTAB, host, host, dev);
X	else
X		(void) sprintf(command, DD_CMD, DUMPTAB, dev);
X	printf("Writing place holder file for %s to tape\n", dt->dt_name);
X	if (status = (system(command) >> 8)) {
X		fprintf(stderr, "%s: %s exitted abnormally (%d).  Restart %s\n", self, command, status, self);
X		exit(1);
X	}
X}
X
X/*
X * setday -- set tm..tm_wday to value consistent with argument
X *
X *	tm.tm_wday has a legal range of 0 (for Sunday) to 6 (for Saturday).
X *	day is a three character string ("sun", "mon", etc) used to determine
X *	the value of tm.tm_wday.
X *
X *	parameters:
X *		day (IN/OUT) -- day of week string
X *	returns:
X *		pointer to static tm struct
X *	side effects:
X *		changes day string to all lower case
X *	deficiencies:
X *		any error causes an error exit from the program
X */
Xstruct tm *
Xsetday(day)
Xchar	*day;
X{
X	static struct tm	mtm;
X	char			*p;
X
X	for (p = day; *p; p++)
X		if (isupper(*p))
X			*p += ('a' - 'A');
X	if (equal(day, "sun"))
X		mtm.tm_wday = 0;
X	else if (equal(day, "mon"))
X		mtm.tm_wday = 1;
X	else if (equal(day, "tue"))
X		mtm.tm_wday = 2;
X	else if (equal(day, "wed"))
X		mtm.tm_wday = 3;
X	else if (equal(day, "thu"))
X		mtm.tm_wday = 4;
X	else if (equal(day, "fri"))
X		mtm.tm_wday = 5;
X	else if (equal(day, "sat"))
X		mtm.tm_wday = 6;
X	else {
X		fprintf(stderr, "%s: setday: illegal day of week (%s)\n", self, day);
X		fprintf(stderr, "\tLegal values are sun, mon, tue, etc.\n");
X		exit(1);
X	}
X	return(&mtm);
X}
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > st.8
X.TH ST 8 "UofI CSO"
X.SH NAME
Xst \- set mdump tapes to correct file for restores
X.SH SYNOPSIS
X.B st
X[
X\fB\-f \fI[host:]/dev/rmtx\fR
X]
X.I filesystem
X.SH DESCRIPTION
X.I St
Xpositions tapes created by
X.IR mdump (8)
Xto the tape file corresponding to
X.IR filesystem .
XFrom there ordinary commands like
X.IR restore (8)
Xcan be used to retrieve files, etc.
X.PP
XThe tape device is selected with the
X.B \-f
Xoption.
XRemote tape reads over the network is accomplished by specifying the device
Xname in the same manner as
X.IR rdump (8),
Xe.g., uiucuxc:/dev/rmt0.
X.PP
XThe order of filesystems on
X.I mdump
Xtapes is determined by a copy of
X.I /etc/dumptab
Xwritten as the first tape file.
X.SH FILES
X/etc/dumptab
X.SH "SEE ALSO"
Xdumptab(5), dump(8), mdump(8), rdump(8)
X.SH BUGS
XIt's not clear this is the best approach, however it was the fastest to
Ximplement.
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > st.c
X/*
X * st -- set tape position on a multi-file dump volume to that of
X *	 the specified filesystem
X *
X * usage: st [-f [host:]/dev/rmtx] [-db] filesystem
X *
X * -f [host:]/dev/rmtx	tape device name.  If the host: prefix
X *	is used, the backup tape will be addressed over the network to
X *	the tape drive on the named host.
X *
X * -db	Write debug information to stderr
X *
X * Written by Paul Pomes, University of Illinois, Computing Services Office
X * Copyright 1985 by Paul Pomes and University of Illinois Board of Trustees
X *
X * This program may be freely reproduced and distributed provided the
X * copyright notice above is included in all copies.  It may not be
X * included in any software product or distribution sold for profit
X * without the permission of the author.
X *
X * UUCP:	{ihnp4,pur-ee,convex}!uiucdcs!uiucuxc!paul
X * ARPANET:	paul%uiucuxc@uiuc.arpa
X * CSNET:	paul%uiucuxc@uiuc.csnet
X * ICBM:	40 07 N / 88 13 W
X * US Mail:	Univ of Illinois, CSO, 1304 W Springfield Ave, Urbana, IL  61801
X *
X * $Log:	st.c,v $
X * Revision 1.2  85/07/11  13:27:51  paul
X * Updated author information.   -pbp
X * 
X * Revision 1.1  85/07/02  13:48:08  root
X * Initial revision
X * 
X */
X
X#ifndef lint
Xstatic char	RcsId[] = "$Header: st.c,v 1.2 85/07/11 13:27:51 paul Exp $";
X#endif
X
X#include	<stdio.h>
X#include	<strings.h>
X#include	<sys/file.h>
X#include	<sys/types.h>
X#include	<sys/mtio.h>
X#include	<sys/ioctl.h>
X#include	<dumptab.h>
X
X#define	equal(s1, s2)	(strcmp(s1, s2) == 0)
X
X/* default tape devices */
X#define		DEF_DEV		"/dev/rmt0"
X#define		DEF_NRDEV	"/dev/nrmt0"
X
X/* size of shorter strings, e.g., host[] */
X#define		SSTR_SIZE	12
X
X/* size of longer strings, e.g., command[] */
X#define		LSTR_SIZE	160
X
X/* commands to read the /etc/dumptab file in tape file #0.
X * argument to DD_CMD is nrdevice; arguments to RDD_CMD are host, nrdevice,
X * and host again.
X */
X#define		DD_CMD		"dd if=%s of=/tmp/xyzzy.mdump bs=512"
X#define		RDD_CMD		"rsh %s dd if=%s of=/tmp/xyzzy.mdump bs=512; rcp %s:/tmp/xyzzy.mdump /tmp/xyzzy.mdump"
X
X/* copy of argv[0] for error messages */
Xchar		*self;
X
X/* debug messages printed if set */
Xint		debug;
X
X/* mag tape stuff */
Xstruct mt_cmds {
X	char	*c_name;
X	int	c_code;
X	int	c_ronly;
X} com[] = {
X	{ "eof",	MTWEOF,	0 },
X	{ "fsf",	MTFSF,	1 },
X	{ "rew",	MTREW,	1 },
X	{ "status",	MTNOP,	1 },
X	{ 0 }
X};
X
Xmain(argc, argv)
Xint	argc;
Xchar	**argv;
X{
X	/* a useful counter */
X	int		i;
X
X	/* tape device */
X	char		*device, *nrdevice;
X
X	/* filesystem to position to */
X	char		*filesys;
X
X	/* host machine */
X	char		host[SSTR_SIZE];
X
X	/* dumptab info */
X	struct dumptab	*dt;
X
X	/* library routines */
X	char		*malloc();
X
X	/* squirrel a copy of *argv[0] away for use in error messages */
X	self = malloc((unsigned) (strlen(*argv) + 1));
X	(void) strcpy(self, *argv);
X
X	host[0] = '\0';
X	device = DEF_DEV;
X	nrdevice = DEF_NRDEV;
X	/*
X	 * parse arguments
X	 */
X	i = 1;
X	while (i < argc && *argv[i] == '-') {
X		if (equal(argv[i]+1, "db")) {
X			/* db - set debug level */
X			debug++;
X			i++;
X			fprintf(stderr, "%s: debug option enabled\n", self);
X		}
X		else if (equal(argv[i]+1, "f"))
X		{
X			/*
X			 * f - read tape device from next argument
X			 * turn /dev/rmt names into /dev/nrmt
X			 */
X			char	temp[40], *p1, *p2;
X
X			i++;
X			p2 = (char *) NULL;
X			if ((p1 = index(argv[i], ':')) != 0) {
X				(void) strncpy(host, argv[i], p1-argv[i]+1);
X				*(index(host, ':')) = '\0';
X				p1++;
X			}
X			if (p1 == 0)
X				p1 = argv[i];
X			device = malloc((unsigned) (strlen(p1) + 1));
X			(void) strcpy(device, p1);
X			if ((p2 = rindex(argv[i], '/')) == 0) {
X				fprintf(stderr, "%s: Device must be a tape drive, e.g., /dev/rmt1, uxc:/dev/rmt2\n", self);
X				exit(1);
X			}
X			p2++;
X			(void) strncpy(temp, p1, p2-p1);
X			*(temp + (p2-p1)) = '\0';
X			nrdevice = malloc((unsigned) (strlen(argv[i])+2));
X			if (*p2 == 'n')
X				(void) sprintf(nrdevice, "%s%s", temp, p2);
X			else
X				(void) sprintf(nrdevice, "%sn%s", temp, p2);
X			if (debug)
X				fprintf(stderr, "host %s, device %s, nrdevice %s\n", host, device, nrdevice);
X			i++;
X		}
X		else
X		{
X			/* command line errors */
X			fprintf(stderr, "%s: %s - bad flag\n", self, argv[i]+1);
X		    Usage:
X			fprintf(stderr, "Usage: %s [-f [host:]/dev/rmtx] [-db] filesystem\n", self);
X			exit(1);
X		}
X	}
X	/* last argument is filesystem name */
X	if (argv[i] == NULL) {
X		fprintf(stderr, "%s: No filesystem specified\n", self);
X		goto Usage;
X	}
X	else {
X		filesys = malloc((unsigned) (strlen(argv[i]) + 1));
X		(void) strcpy(filesys, argv[i]);
X		if (debug)
X			fprintf(stderr, "filesys %s\n", filesys);
X	}
X	read_dtinfo(host, device);
X	setdtfile("/tmp/xyzzy.mdump");
X	setdtent();
X	if (dt = getdtnam(filesys)) {
X		printf("Skipping %d tape files...", dt->dt_position);
X		(void) fflush(stdout);
X		for (i = 0; i < dt->dt_position; i++) {
X			mtio(host, nrdevice, "fsf");
X			printf("%d ", i+1);
X			(void) fflush(stdout);
X		}
X		printf("done.\n");
X		(void) unlink("/tmp/xyzzy.mdump");
X		exit(0);
X	}
X	fprintf(stderr, "%s: %s not in tape file #0 (and thus not on tape)\n",
X		self, filesys);
X	fprintf(stderr, "\tCopy of tape file #0 in /tmp/xyzzy.mdump\n");
X	exit(1);
X}
X
X/*
X * mtio -- perform a local/remote tape operation
X *
X *	for local tapes (host[0] == '\0') use the ioctl calls.
X *	remote tapes use an rsh command.
X *
X *	parameters:
X *		host (IN) -- name of remote host
X *		dev (IN) -- tape drive name
X *		cp (IN) -- tape opcode
X *	returns:
X *		none
X *	side effects:
X *		none
X *	deficiencies:
X *		any error causes an error exit from the program
X */
Xmtio(host, dev, cp)
Xchar	*host, *dev, *cp;
X{
X	struct mt_cmds	*comp;
X
X	/* tape file descriptor */
X	int		mtfd;
X
X	/* return code from system() call */
X	int		status;
X
X	/* from sys/mtio.h */
X	struct mtop	mt_com;
X
X	/* buffer for rsh command */
X	char		command[LSTR_SIZE];
X
X	if (*host == '\0') {
X		for (comp = com; comp->c_name != NULL; comp++)
X			if (strncmp(cp, comp->c_name, strlen(cp)) == 0)
X				break;
X		if (comp->c_name == NULL) {
X			fprintf(stderr, "%s: mtio: don't grok \"%s\"\n", self, cp);
X			exit(1);
X		}
X		if ((mtfd = open(dev, comp->c_ronly ? 0 : 2)) < 0) {
X			fprintf(stderr, "%s: mtio: open of ", self);
X			perror(dev);
X			exit(1);
X		}
X		mt_com.mt_op = comp->c_code;
X		mt_com.mt_count = 1;
X		if (ioctl(mtfd, MTIOCTOP, &mt_com) < 0) {
X			fprintf(stderr, "%s: mtio: %s %s %d ", self, dev,
X				comp->c_name, mt_com.mt_count);
X			perror("failed");
X			exit(1);
X		}
X		(void) close(mtfd);
X	}
X	else {
X		(void) strcpy(command, "rsh ");
X		(void) strcat(command, host);
X		(void) strcat(command, " mt -t ");
X		(void) strcat(command, dev);
X		(void) strcat(command, " ");
X		(void) strcat(command, cp);
X		if (debug)
X			fprintf(stderr, "tape command %s\n", command);
X		if (status = (system(command) >> 8)) {
X			fprintf(stderr, "%s: %s exitted abnormally (%d).  Restart %s\n", self, command, status, self);
X			exit(1);
X		}
X	}
X}
X
X/*
X * read_dtinfo -- copy the dumptab file contained within tape file #0
X *
X *	the /etc/dumptab file used to create the backup tape volume
X *	is contained within the first tape file.  this routine copies
X *	it to the magic name of /tmp/xyzzy.mdump on the current host.
X *
X *	parameters:
X *		host (IN) -- name of remote host
X *		dev (IN) -- tape drive name
X *	returns:
X *		none
X *	side effects:
X *		none
X *	deficiencies:
X *		any error causes an error exit from the program
X */
Xread_dtinfo(host, dev)
Xchar	*host, *dev;
X{
X	char	command[LSTR_SIZE];
X	int	status;
X
X	mtio(host, dev, "rew");
X	if (*host)
X		(void) sprintf(command, RDD_CMD, host, dev, host);
X	else
X		(void) sprintf(command, DD_CMD, dev);
X	printf("Reading /etc/dumptab info from first tape file.\n");
X	if (status = (system(command) >> 8)) {
X		fprintf(stderr, "%s: %s exitted abnormally (%d).  Restart %s\n", self, command, status, self);
X		exit(1);
X	}
X}
SHAR_EOF
exit 0