[net.sources] tapemgr -- A program for controling tape drive access.

thompson@dalcs.UUCP (11/10/86)

	I wrote this program to aviod the problem of someone else
    accessing the tape drive on our system while it was in use by
    someone else. I hope others find it useful.

---------------------------------Cut Here-----------------------------
#!/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
#	README
#	tapemgr.1
#	tapemgr.c
# This archive created: Mon Nov 10 14:00:50 1986
export PATH; PATH=/bin:$PATH
echo shar: extracting "'Makefile'" '(2020 characters)'
if test -f 'Makefile'
then
	echo shar: over-writing existing file "'Makefile'"
fi
sed 's/^X//' << \SHAR_EOF > 'Makefile'
XDEST	      = /usr/local
X
XCFLAGS        = -O
X
XEXTHDRS	      = /usr/include/ctype.h \
X		/usr/include/machine/param.h \
X		/usr/include/pwd.h \
X		/usr/include/sgtty.h \
X		/usr/include/signal.h \
X		/usr/include/stdio.h \
X		/usr/include/strings.h \
X		/usr/include/sys/ioctl.h \
X		/usr/include/sys/mtio.h \
X		/usr/include/sys/param.h \
X		/usr/include/sys/stat.h \
X		/usr/include/sys/time.h \
X		/usr/include/sys/ttychars.h \
X		/usr/include/sys/ttydev.h \
X		/usr/include/sys/types.h \
X		/usr/include/sys/wait.h
X
XHDRS	      =
X
XINSFLAGS      = -m 04555 -o root -g tapemgr
X
XLDFLAGS	      = -O
X
XLIBS	      =
X
XLINKER	      = cc
X
XLINTFLAGS     =
X
XLINTLIST      =
X
XMAKEFILE      = Makefile
X
XOBJS	      = tapemgr.o
X
XPRINT	      = lpr -p -i
X
XPROGRAM	      = tapemgr
X
XSRCS	      = tapemgr.c
X
XVERSION	      =
X
Xall:		$(PROGRAM)
X
X$(PROGRAM):     $(OBJS) $(LIBS)
X		@echo -n "Loading $(PROGRAM) ... "
X		@$(LINKER) $(LDFLAGS) $(OBJS) $(LIBS) -o $(PROGRAM)
X		@echo "done"
X
Xclean:;		@rm -f $(OBJS)
X
Xco:;		@co -r$(VERSION) $(HDRS) $(SRCS)
X
Xdepend:;	@mkmf -f $(MAKEFILE) PROGRAM=$(PROGRAM) DEST=$(DEST)
X
Xdiff:;		@rcsdiff -r $(VERSION) $(HDRS) $(SRCS)
X
Xindex:;		@ctags -wx $(HDRS) $(SRCS)
X
Xinstall:	$(PROGRAM)
X		@echo Installing $(PROGRAM) in $(DEST)
X		@install -s $(INSFLAGS) $(PROGRAM) $(DEST)
X
Xlint:;		@lint $(LINTFLAGS) -Dlint $(SRCS) $(LINTLIST)
X
Xprint:;		@$(PRINT) $(HDRS) $(SRCS)
X
Xprogram:        $(PROGRAM)
X
Xtags:           $(HDRS) $(SRCS); @ctags $(HDRS) $(SRCS)
X
Xupdate:		$(DEST)/$(PROGRAM)
X
X$(DEST)/$(PROGRAM): $(SRCS) $(LIBS) $(HDRS) $(EXTHDRS)
X		@make -f $(MAKEFILE) DEST=$(DEST) install
X
X.DEFAULT:;	@co -r$(VERSION) $@
X###
Xtapemgr.o: /usr/include/ctype.h /usr/include/pwd.h /usr/include/stdio.h \
X	/usr/include/strings.h /usr/include/sys/ioctl.h \
X	/usr/include/sys/ttychars.h /usr/include/sys/ttydev.h \
X	/usr/include/sgtty.h /usr/include/sys/param.h \
X	/usr/include/machine/param.h /usr/include/signal.h \
X	/usr/include/sys/types.h /usr/include/sys/mtio.h \
X	/usr/include/sys/stat.h /usr/include/sys/time.h \
X	/usr/include/sys/wait.h
SHAR_EOF
if test 2020 -ne "`wc -c 'Makefile'`"
then
	echo shar: error transmitting "'Makefile'" '(should have been 2020 characters)'
fi
echo shar: extracting "'README'" '(1952 characters)'
if test -f 'README'
then
	echo shar: over-writing existing file "'README'"
fi
sed 's/^X//' << \SHAR_EOF > 'README'
X
X	Tapemgr is a program that tries to avoid the problem of
X    having someone else access the tape drive you have your tape
X    mounted on.
X
X	This program has only been run/tested on a BSD4.2 operating
X    system and I expect that it will not run without modification on
X    another type of operating system. Also it requires getopt which
X    has been distributed over the network several times, with other
X    programs and alone so I haven't bothered sticking it in here.
X
X	To install this program there are a number of things you
X    should do.
X
X	1) Set up a group called tapemgr. Add those people who may
X    need special privileges with the tape drive(ignoring timeout and
X    reseting drives not allocated to them.) i.e.
X    "tapemgr:*:300:root,sys1,thompson,wicat"
X	2) Set up an account called tapemgr that is in the group
X    tapemgr. i.e. "tapemgr:*:30:300:Magnetic Tape Manager:/:/bin/csh"
X	3) Go through the defines at the beginning of tapemgr.c and
X    change any that need changing (the pid and gid of tapemgr for
X    starters if you didn't use 30 and 300 and the number of drives
X    you actually have if you have more than one) the comments with
X    the various defines will tell you what they are for.
X	4) Compile and install tapemgr "make install".
X	5) Install the manual entry tapemgr.1
X	6) Create the log file if you want a log kept.
X	7) Change the owner and group of /dev/mt* and /dev/rmt* to
X    tapemgr and change /dev/nmt* and /dev/nrmt* to symbolic links to
X    the appropriate /dev/mt* and /dev/rmt* (e.g. /dev/nmt0 becomes a
X    symbolic link to /dev/mt4). Also change protection on all
X    /dev/mt* and /dev/rmt* to 600. NOTE: except for the symbolic
X    link stuff everything(changing ownership, group and mode) could
X    be done with the command "/usr/local/tapemgr -v -r all".
X	8) Add the following two lines to your /etc/rc.local.
X			echo cleaning up tape drives.		>/dev/console
X/usr/local/tapemgr -v -r all					>/dev/null
SHAR_EOF
if test 1952 -ne "`wc -c 'README'`"
then
	echo shar: error transmitting "'README'" '(should have been 1952 characters)'
fi
echo shar: extracting "'tapemgr.1'" '(2871 characters)'
if test -f 'tapemgr.1'
then
	echo shar: over-writing existing file "'tapemgr.1'"
fi
sed 's/^X//' << \SHAR_EOF > 'tapemgr.1'
X.TH TAPEMGR 1 "4 April 1986"
X.SH NAME
Xtapemgr \- manage tape drives, by restricting access to only one user.
X.SH SYNOPSIS
X.I tapemgr
X.I "[-r drive|all] [-n #drives|all] [-c command] [-v] [-t]"
X.SH DESCRIPTION
XNormally the requested number of drives are allocated then a command is
Xexecuted and once the command has terminated
Xthe drives are deallocated, one of the actions taken by
X.I tapemgr
Xduring deallocation is to make sure the drive is dismounted and offline.
X.TP 5
X.I "-r drive"
X.PD 0
X.TP 5
X.I "-r all"
X.PD
XResets the specified drive or all drives.
XIf you do not own the specified
Xdrive then you must be
X.I root
Xor in the group
X.I tapemgr
Xto reset the drive.
XThe option may appear more than once on the command line,
Xand the drives specified are accumulated.
XOnce the option is specified the drives specified will be reset and
Xno other actions taken.
X.TP 5
X.I "-n #drives"
X.PD 0
X.TP 5
X.I "-n all"
X.PD
XThis option tells tapemgr how many drives to allocate (the default is 1).
XThis option may also appear more than once on the command line but only
Xthe last occurrence has any ultimate effect.
X.TP 5
X.I "-c command"
XNormally
X.I tapemgr
Xstarts up
X.I /bin/sh
Xor the shell specified in the environment variable
X.I SHELL.
XIf the
X.I -c
Xoption is specified then the command specified will be will be executed
Xinstead of shell.
X.TP 5
X.I "-v"
XThis option toggles verbose mode, initially verbose mode is on.
X.TP 5
X.I "-t"
XNormally
X.I tapemgr
Xhas a timeout of 6 hours, if you are
X.I root
Xor in the group
X.I tapemgr
Xyou can request that the timeout be turned off with the -t option.
X.SH EXAMPLES
X.TP 5
X.I "tapemgr -r all"
Xresets all the tape drives on the system, this command should appear in
X.I /etc/rc.local.
X.TP 5
X.I "tapemgr"
Xallocates one drive.
X.TP 5
X.I "tapemgr -r 1"
Xresets drive 1. NOTE: drives are numbered from 0.
X.TP 5
X.I "tapemgr -n 2"
Xrequests 2 tape drives.
X.TP 5
X.I "tapemgr -n all"
Xrequests all tape drives.
X.TP 5
X.I "tapemgr -c 'csh -f' -v -t"
Xrequests 1 tape drive executing the c-shell with the -f option turning off
Xverbose mode and the time out.
X.SH ENVIRONMENT
X.TP 5
X.I TAPE_DRIVES
XThe numbers of the drives selected in a blank separated list. This environment
Xvariable is created by
X.I tapemgr
Xif it does not exist and is appended to if it does.
X.TP 5
X.I SHELL
XUsed by
X.I tapemgr
Xfor a default program to start up if the -c option is not specified.
X.SH FILES
X.TP 5
X.I "/usr/adm/tapelog"
XA log of all the activities of tapemgr. No logging is done if the file
Xdoes not exist.
X.TP 5
X.I "/dev/rmt*"
Xcharacter special tape devices.
X.TP 5
X.I "/dev/mt*"
Xblock special tape devices.
X.SH AUTHOR
XMichael Thompson at Dalhousie University, Halifax N.S. Canada.
X.SH DIAGNOSTICS
XI hope they are self explanatory.
X.SH BUGS
XNone that I know of, but this program has not been tested on a system with
Xmore than one drive, so that might be where the troubles will start.
SHAR_EOF
if test 2871 -ne "`wc -c 'tapemgr.1'`"
then
	echo shar: error transmitting "'tapemgr.1'" '(should have been 2871 characters)'
fi
echo shar: extracting "'tapemgr.c'" '(24155 characters)'
if test -f 'tapemgr.c'
then
	echo shar: over-writing existing file "'tapemgr.c'"
fi
sed 's/^X//' << \SHAR_EOF > 'tapemgr.c'
X#ifndef lint
Xstatic char rcsid[] =
X            "$Header: tapemgr.c,v 2.3 86/08/15 13:19:39 thompson Exp $";
X#endif
X/*
X *
X *			T A P E M G R
X *
X * AUTHOR:  Written by Michael A . Thompson at Dalhousie University
X *	Department of Mathematics, Statistics and Computing Science.
X *
X * PURPOSE: To restrict access to tapedrives to one user at a time.
X *	See tapemgr.1 for details of usage.
X *
X * NOTICE: This is free software, do with it what you will.
X *	
X */
X 
X/*
X *	$Locker:  $
X *	$Source: /usr/src/local/tapemgr/RCS/tapemgr.c,v $
X *
X * $Log:	tapemgr.c,v $
X * Revision 2.3  86/08/15  13:19:39  thompson
X * shorter logmessage
X * 
X * Revision 2.2  86/04/08  04:27:06  thompson
X * fix comments.
X * 
X * Revision 2.1  86/03/25  11:50:59  thompson
X * author info.
X * 
X * Revision 2.0  86/03/24  23:50:30  thompson
X * First, release.
X * 
X * Revision 1.1  86/03/22  23:23:43  thompson
X * Initial revision
X * 
X */
X#include	<ctype.h>
X#include	<pwd.h>
X#include	<stdio.h>
X#include	<strings.h>
X#include	<sys/ioctl.h>
X#include	<sys/param.h>
X#include	<sys/mtio.h>
X#include	<sys/stat.h>
X#include	<sys/time.h>
X#include	<sys/wait.h>
X
X#define RESTORE_TERMINAL	/* Define this if you want the terminal
X				   state restored upon termination */
X#define KERNAL_PAGER		/* Define this if you have the kernal
X				   pager installed on you system
X				   <2037@utcsstat.UUCP> */
X
X#define MAX_DRIVES	4	/* The maximum number of drives that your
X				   system is designed to support. */
X#define NUM_DRIVES	1	/* The number of drives that you system
X				   actually has. */
X#define MAX_RETRIES	5	/* How many times the program will attempt
X				   to allocate the requested number of
X				   drives after finding that no more are
X				   currently available (NOTE: it sleeps
X				   for SLEEP3 seconds between attempts */
X
X#ifndef DEBUG
X#define SLEEP1		1	/* Short sleep */
X#define SLEEP2		5	/* Med. sleep */
X#define SLEEP3		120	/* Long sleep */
X#define TIME_OUT	(60 * 60 * 6)
X				/* 6 hours -- how long before timeout
X				   occurs */
X#define WARNING		(60 * 5)/* 5 minutes -- how long after timeout
X				   occures before everything is actually
X				   shutdown */
X#else DEBUG
X#define SLEEP1		1
X#define SLEEP2		5
X#define SLEEP3		5
X#define TIME_OUT	(45)
X#define WARNING		(15)
X#endif DEBUG
X
X
X/* the following defines calculate the various device numbers for a
X   given drive x: AR indicates auto-rewind on close, nnnBPI indicates the
X   number of bit per inch. */
X#define AR_800BPI(x)	(0 + (x - 1))
X#define NAR_800BPI(x)	(AR_800BPI(x) + MAX_DRIVES)
X#define AR_1600BPI(x)	(NAR_800BPI(x) + MAX_DRIVES)
X#define NAR_1600BPI(x)	(AR_1600BPI(x) + MAX_DRIVES)
X#define AR_6250BPI(x)	(NAR_1600BPI(x) + MAX_DRIVES)
X#define NAR_6250BPI(x)	(AR_6250BPI(x) + MAX_DRIVES)
X
X#define MT_NAME		"/dev/mt"/* prefix for the block devices */
X#define RMT_NAME	"/dev/rmt"/* prefix for the raw devices */
X
X#define ROOT		0	/* uid of root */
X#define TAPEMGR_UID	30	/* uid of the user tapemgr */
X#define TAPEMGR_GID	300	/* gid of the user tapemgr */
X
X#define DRIVE_PROT	0600	/* the protection modes for the tape
X				   devices */
X
X#define SHELL		"/bin/sh"/* the default shell */
X
X#ifndef DEBUG
X#define LOGFILE		"/usr/adm/tapelog"
X				/* where the log is kept, if this file
X				   does not exist no log is kept */
X#else DEBUG
X#define LOGFILE		"tapelog"
X#endif
X
X#define ENV_VAR		"TAPE_DRIVES"
X				/* Environment variable to inform children
X				   about which drives are available */
X#define ENV_VAR_EQ	"TAPE_DRIVES="
X				/* This should be the same as ENV_VAR with
X				   an = tagged on */
X
X#define NO_SIGNAL	EOF	/* these are just used as fill for when
X				   clean_up is called directly rather than
X				   by an interupt */
X#define NO_CODE		EOF
X#define NO_CONTEXT	((struct sigcontext *) EOF)
X
X#define TRUE		(logical) '\1'
X#define FALSE		(logical) '\0'
Xtypedef char    logical;
X
Xstruct mtop mtoffl = {		/* tape operation offline */
X    MTOFFL, 1
X};
X
Xint     otpgrp;			/* for storing and restoring the state of
X				   the terminal */
X#ifdef RESTORE_TERMINAL
Xint     oldisc;
Xstruct sgttyb   otty;
Xstruct tchars   otchars;
X#ifdef KERNAL_PAGER
Xstruct tpage    otpage;
X#endif KERNAL_PAGER
Xint     olocalm;
Xstruct ltchars  oltchars;
X#endif RESTORE_TERMINAL
X
Xint     fd_tty = EOF;		/* equal to the fd of the terminal */
X
Xstruct sigvec   ovec;
Xstruct sigvec   vec_dfl = {	/* to restore the original signal handler 
X				*/
X    SIG_DFL, 0, 0
X};
Xint     kill_and_clean (), clean_up (), timeout ();
Xstruct sigvec   vec_kill_and_clean = {
X				/* to cause kill_and_clean to be called
X				   when a signal is received */
X    kill_and_clean, 0, 0
X};
Xstruct sigvec   vec_clean_up = {/* to cause clean_up to be called when a
X				   signal is received */
X    clean_up, 0, 0
X};
Xstruct sigvec   vec_timeout = {	/* to cause timeout to be called when a
X				   signal is received */
X    timeout, 0, 0
X};
X
Xstruct itimerval    time_out,	/* the values for the interval timer */
X                    ovalue;
X
Xint     pid;			/* the pid of the child */
X
Xchar  **tapemgr_env = 0;	/* the environment to be passed to the
X				   child */
X
Xlogical allocated[MAX_DRIVES + 1];/* which drives have been allocated
X				   allocated[0] == TRUE means that reset
X				   is being performed and no allocations
X				   are to be performed */
X
Xlogical verbose = TRUE;		/* TRUE for a mouthy program */
X
Xchar   *getenv (), *getlogin (), *malloc (), *realloc (), *calloc ();
X
X/*---------------------------------------------------------------------------
X * 
X *			P R I V I L E G E D _ U S E R
X *
X *	This program returns TRUE if the user who is running it is root or
X * is in the group tapemgr.
X *
X *      Entry Conditions:        none.
X *
X *      Exit Conditions:         once the status of the person running the
X *				 program has been determined.
X *
X *      Returns:                 TRUE iff the user is privileged
X *				 FALSE otherwise.
X *
X *      Side Effects:            none.
X *
X *      Static Variables:        first_time, gidset, ngrps, cur_grp,
X *				 privileged
X *
X *      External Variables:      none.
X *
X *      Non-library Functions:   none.
X *
X *---------------------------------------------------------------------------
X */
Xlogical privileged_user () {
X    static  logical first_time = TRUE;
X    static int  gidset[NGROUPS],
X                ngrps,
X                cur_grp;
X    static  logical privileged = FALSE;
X
X    if (first_time && !(privileged = getuid () == ROOT)) {
X	ngrps = getgroups (NGROUPS, gidset);
X	first_time = FALSE;
X	for (cur_grp = 0; cur_grp < ngrps && !privileged; cur_grp++)
X	    privileged = gidset[cur_grp] == TAPEMGR_GID;
X    }
X    return privileged;
X}
X
X/*---------------------------------------------------------------------------
X * 
X *			L O G M E S G
X *
X *	Log an action in the tapelog file if it exists.
X *
X *      Entry Conditions:        none.
X *
X *      Exit Conditions:         none.
X *
X *      Returns:                 nothing.
X *
X *      Side Effects:            none.
X *
X *      Static Variables:        none.
X *
X *      External Variables:      none.
X *
X *      Non-library Functions:   none.
X *
X *---------------------------------------------------------------------------
X */
Xvoid logmesg (action, drive)
Xchar   *action;
Xint     drive;
X{
X    FILE * logfile;
X    struct stat logfile_stat;
X    struct timeval  tp;
X    struct timezone tzp;
X    struct passwd  *pwdent;
X
X    if (stat (LOGFILE, &logfile_stat))
X#ifndef DEBUG
X	    return;
X#else DEBUG
X	    perror (LOGFILE);
X#endif DEBUG
X    (void) gettimeofday (&tp, &tzp);
X    pwdent = getpwuid (getuid ());
X    if (!(logfile = fopen (LOGFILE, "a")))
X	return;
X    fprintf (logfile,
X#ifndef DEBUG
X	    "%d,%s,%s/%s(%d),%s",
X#else DEBUG
X	    "drive=%d,action=%s,login/user(uid)=%s/%s(%d),date and time=%s",
X#endif DEBUG
X	    drive - 1, action, getlogin (), pwdent -> pw_name,
X	    getuid (), ctime ((time_t *) & tp.tv_sec));
X    (void) fclose (logfile);
X}
X
X/*---------------------------------------------------------------------------
X * 
X *			K I L L _ A N D _ C L E A N
X *
X *	Kill the children.
X *
X *      Entry Conditions:        SIGALRM
X *
X *      Exit Conditions:         all children killed.
X *
X *      Returns:                 nothing.
X *
X *      Side Effects:            resets the handler for SIGALRM and turns off
X *				 the interval timer.
X *
X *      Static Variables:        none.
X *
X *      External Variables:      pid, vec_dfl, verbose, otpgrp.
X *
X *      Non-library Functions:   logmesg.
X *
X *---------------------------------------------------------------------------
X */
Xint     kill_and_clean (sig, code, scp)
Xint     sig,
X        code;
Xstruct sigcontext  *scp;
X{
X    int     cur_tpgrp,
X            prev_tpgrp;
X
X    (void) setitimer (ITIMER_REAL, &ovalue, &time_out);
X    (void) sigvec (SIGALRM, &vec_dfl, &ovec);
X    if (pid) {
X	if (sig == SIGALRM) {
X	    logmesg ("Timeout", 0);
X	    fprintf (stderr, "Timeout has arrived!!!!\007\n");
X	}
X	else {
X	    logmesg ("Killed", 0);
X	    fprintf (stderr, "I been shot jim!!!\007\n");
X	}
X	if (fd_tty != EOF) {
X	    if (verbose) {
X		printf ("Take That(pid=%d)", pid);
X		(void) fflush (stdout);
X	    }
X	    (void) kill (pid, SIGHUP);
X	    sleep (SLEEP2);
X	    (void) kill (pid, SIGKILL);
X	    sleep (SLEEP2);
X	    (void) ioctl (fd_tty, (int) TIOCGPGRP, (char *) & cur_tpgrp);
X	    for (prev_tpgrp = EOF;
X		    cur_tpgrp != prev_tpgrp && cur_tpgrp != otpgrp;) {
X		if (verbose) {
X		    printf ("...and That(pgrp=%d)", cur_tpgrp);
X		    (void) fflush (stdout);
X		}
X		(void) killpg (cur_tpgrp, SIGHUP);
X		sleep (SLEEP2);
X		(void) killpg (cur_tpgrp, SIGKILL);
X		prev_tpgrp = cur_tpgrp;
X		sleep (SLEEP2);
X		(void) ioctl (fd_tty, (int) TIOCGPGRP, (char *) & cur_tpgrp);
X	    }
X	    if (verbose)
X		printf ("...I think it's dead now.\n");
X	}
X    }
X}
X
X/*---------------------------------------------------------------------------
X * 
X *			T I M E O U T
X *
X *	nothing much really, just sets things up for the kill.
X *
X *      Entry Conditions:        SIGALRM
X *
X *      Exit Conditions:         none.
X *
X *      Returns:                 nothing.
X *
X *      Side Effects:            sets the handler for SIGALRM to
X *				 kill_and_clean.
X *
X *      Static Variables:        none.
X *
X *      External Variables:      vec_kill_and_clean.
X *
X *      Non-library Functions:   none.
X *
X *---------------------------------------------------------------------------
X */
Xint     timeout (sig, code, scp)
Xint     sig,
X        code;
Xstruct sigcontext  *scp;
X{
X    fprintf (stderr,
X	    "You have %d minutes %d seconds in which to clean up, timeout has arrived.\007\n",
X	    (int) WARNING / 60, WARNING % 60);
X    (void) sigvec (SIGALRM, &vec_kill_and_clean, &ovec);
X}
X
X/*---------------------------------------------------------------------------
X * 
X *			S E T _ D E V I C E
X *	Sets the ownership and protection of the specified device, if chutzpah
X * is set then it just pretends to set the ownership and protection.
X *
X *      Entry Conditions:        none.
X *
X *      Exit Conditions:         none.
X *
X *      Returns:                 nothing.
X *
X *      Side Effects:            none.
X *
X *      Static Variables:        none.
X *
X *      External Variables:      verbose.
X *
X *      Non-library Functions:   none.
X *
X *---------------------------------------------------------------------------
X */
Xvoid set_device (drive, uid, silently, chutzpah)
Xchar   *drive;
Xint     uid;
Xlogical silently, chutzpah;
X{
X    struct stat drive_stat;
X
X    if (stat (drive, &drive_stat)) {
X	if (!silently || chutzpah)
X	    if (verbose)
X		printf ("%15s", "");
X	return;
X    }
X    if (!chutzpah) {
X	(void) chown (drive, uid, TAPEMGR_GID);
X	(void) chmod (drive, DRIVE_PROT);
X    }
X    if (!silently || chutzpah)
X	if (verbose)
X	    printf ("%15s", drive);
X}
X
X/*---------------------------------------------------------------------------
X * 
X *			S E T _ D R I V E
X *
X *	Runs set_device for every device for a given drive, except for
X * the raw no auto rewind 800bpi device which is run with chutzpah TRUE
X * it is left up to the calling routine to run set_device for that device.
X *
X *      Entry Conditions:        none.
X *
X *      Exit Conditions:         none.
X *
X *      Returns:                 nothing.
X *
X *      Side Effects:            none.
X *
X *      Static Variables:        none.
X *
X *      External Variables:      verbose.
X *
X *      Non-library Functions:   set_device.
X *
X *---------------------------------------------------------------------------
X */
Xvoid set_drive (drive, uid)
Xint     drive,
X        uid;
X{
X    char    cur_drive[BUFSIZ];
X
X    if (verbose) {
X	printf ("BLOCK Devices available with drive %d.\n", drive - 1);
X	printf ("%15s%15s%15s%15s\n", "", "800bpi", "1600bpi", "6250bpi");
X	printf ("%15s", "Auto Rewind:");
X    }
X    set_device (sprintf (cur_drive, "%s%d", MT_NAME, AR_800BPI (drive)),
X	    uid, FALSE, FALSE);
X    set_device (sprintf (cur_drive, "%s%d", MT_NAME, AR_1600BPI (drive)),
X	    uid, FALSE, FALSE);
X    set_device (sprintf (cur_drive, "%s%d", MT_NAME, AR_6250BPI (drive)),
X	    uid, FALSE, FALSE);
X    if (verbose)
X	printf ("\n%15s", "No Auto Rewind:");
X    set_device (sprintf (cur_drive, "%s%d", MT_NAME, NAR_800BPI (drive)),
X	    uid, FALSE, FALSE);
X    set_device (sprintf (cur_drive, "%s%d", MT_NAME, NAR_1600BPI (drive)),
X	    uid, FALSE, FALSE);
X    set_device (sprintf (cur_drive, "%s%d", MT_NAME, NAR_6250BPI (drive)),
X	    uid, FALSE, FALSE);
X    if (verbose) {
X	printf ("\nRAW Devices available with drive %d.\n", drive - 1);
X	printf ("%15s%15s%15s%15s\n", "", "800bpi", "1600bpi", "6250bpi");
X	printf ("%15s", "Auto Rewind:");
X    }
X    set_device (sprintf (cur_drive, "%s%d", RMT_NAME, AR_800BPI (drive)),
X	    uid, FALSE, FALSE);
X    set_device (sprintf (cur_drive, "%s%d", RMT_NAME, AR_1600BPI (drive)),
X	    uid, FALSE, FALSE);
X    set_device (sprintf (cur_drive, "%s%d", RMT_NAME, AR_6250BPI (drive)),
X	    uid, FALSE, FALSE);
X    if (verbose)
X	printf ("\n%15s", "No Auto Rewind:");
X    set_device (sprintf (cur_drive, "%s%d", RMT_NAME, NAR_800BPI (drive)),
X	    uid, FALSE, TRUE);
X    set_device (sprintf (cur_drive, "%s%d", RMT_NAME, NAR_1600BPI (drive)),
X	    uid, FALSE, FALSE);
X    set_device (sprintf (cur_drive, "%s%d", RMT_NAME, NAR_6250BPI (drive)),
X	    uid, FALSE, FALSE);
X    if (verbose)
X	printf ("\n");
X}
X
X/*---------------------------------------------------------------------------
X * 
X *			C L E A N _ U P
X *
X *	restore the terminal, and reset all the allocated drives.
X *
X *      Entry Conditions:        SIGINT or called from the main program.
X *
X *      Exit Conditions:         all allocated drives released.
X *
X *      Returns:                 never.
X *
X *      Side Effects:            blocks all signals
X *
X *      Static Variables:        none.
X *
X *      External Variables:      verbose, allocated, mtoffl, oldisc, otty,
X *				 otpgrp, otchars, otpage, olocalm, oltchars.
X *
X *      Non-library Functions:   priviliged_user, logmesg.
X *
X *---------------------------------------------------------------------------
X */
Xint     clean_up (sig, code, scp)
Xint     sig,
X        code;
Xstruct sigcontext  *scp;
X{
X    FILE * fdrive;
X    struct stat drive_stat;
X    int     drive;
X    char    cur_drive[BUFSIZ];
X
X    (void) sigsetmask (~0);
X    if (verbose)
X	printf ("Cleaning up...\n");
X    for (drive = 1; drive <= NUM_DRIVES; drive++)
X	if (allocated[drive]) {
X	    (void) sprintf (cur_drive, "%s%d", RMT_NAME, NAR_800BPI (drive));
X	    if (stat (cur_drive, &drive_stat)) {
X		perror (cur_drive);
X		continue;
X	    }
X	    if (privileged_user () || getuid () == drive_stat.st_uid) {
X		if (verbose)
X		    printf ("Reseting drive %d.\n", drive - 1);
X		if ((fdrive = fopen (cur_drive, "r"))) {
X		    (void) ioctl (fileno (fdrive), (int) MTIOCTOP,
X			    (char *) & mtoffl);
X		    (void) fclose (fdrive);
X		}
X		set_drive (drive, TAPEMGR_UID);
X		set_device (cur_drive, TAPEMGR_UID, TRUE, FALSE);
X		logmesg ("Reset", drive);
X	    }
X	}
X#ifdef RESTORE_TERMINAL
X    if (fd_tty != EOF) {
X	(void) ioctl (fd_tty, (int) TIOCSETD, (char *) & oldisc);
X	(void) ioctl (fd_tty, (int) TIOCSETP, (char *) & otty);
X	(void) ioctl (fd_tty, (int) TIOCSPGRP, (char *) & otpgrp);
X	(void) ioctl (fd_tty, (int) TIOCSETC, (char *) & otchars);
X#ifdef KERNAL_PAGER
X	(void) ioctl (fd_tty, (int) TIOCSPAG, (char *) & otpage);
X#endif KERNAL_PAGER
X	(void) ioctl (fd_tty, (int) TIOCLSET, (char *) & olocalm);
X	(void) ioctl (fd_tty, (int) TIOCSLTC, (char *) & oltchars);
X    }
X#endif RESTORE_TERMINAL
X    exit (0);
X}
X
X/*---------------------------------------------------------------------------
X * 
X *			S E T _ E N V
X *
X *	Adds the specified drive to the environment variable ENV_VAR
X *
X *      Entry Conditions:        none.
X *
X *      Exit Conditions:         none.
X *
X *      Returns:                 nothing.
X *
X *      Side Effects:            modifies tapemgr_env.
X *
X *      Static Variables:        first_time, space.
X *
X *      External Variables:      none.
X *
X *      Non-library Functions:   none.
X *
X *---------------------------------------------------------------------------
X */
Xvoid set_env (drive, envp)
Xint     drive;
Xchar  **envp;
X{
X    static  logical first_time = TRUE;
X    static  logical space = TRUE;
X    unsigned int    count;
X    char  **tenvp,
X          **nenvp,
X            env_buf[BUFSIZ];
X
X    if (!tapemgr_env)
X	if (getenv (ENV_VAR))
X	    tapemgr_env = envp;
X	else {
X	    for (count = 2, tenvp = envp; *tenvp; tenvp++, count++);
X	    tapemgr_env = (char **) calloc (count, sizeof (char *));
X	    for (tenvp = envp, nenvp = tapemgr_env; *tenvp;
X		    *(nenvp++) = *(tenvp++));
X	    *nenvp = ENV_VAR_EQ;
X	    *++nenvp = NULL;
X	    space = FALSE;
X	}
X    for (nenvp = tapemgr_env;
X	    strncmp (*nenvp, ENV_VAR_EQ, strlen (ENV_VAR_EQ)); nenvp++);
X    if (space)
X	(void) sprintf (env_buf, "%s %d", *nenvp, drive);
X    else {
X	(void) sprintf (env_buf, "%s%d", *nenvp, drive);
X	space = TRUE;
X    }
X    if (first_time) {
X	*nenvp = strcpy (malloc ((unsigned int) strlen (env_buf) + 1),
X		env_buf);
X	first_time = FALSE;
X    }
X    else
X	*nenvp = strcpy (realloc (*nenvp,
X		    (unsigned int) strlen (env_buf) + 1),
X		env_buf);
X}
X
Xmain (argc, argv, envp)
Xint     argc;
Xchar  **argv,
X      **envp;
X{
X    struct stat drive_stat;
X    union wait status;
X    int     drive,
X            num_allocated,
X            num_optarg,
X            option,
X            org_sigmask;
X    int     num_drives = 1;
X    int     retries = 0;
X    char   *called = *argv;
X    char    cur_drive[BUFSIZ];
X    char   *tape_command = getenv ("SHELL") ? getenv ("SHELL") : SHELL;
X    extern char *optarg;
X    logical one_allocated;
X    logical t_out = TRUE;
X
X    if (geteuid () != ROOT) {
X	fprintf (stderr,
X		"This program must be suid to root for it to work.\n");
X	exit (13);
X    }
X    while ((option = getopt (argc, argv, "r:n:c:vt")) != EOF)
X	switch (option) {
X	    case 'r': 
X		if (!strcmp (optarg, "all")) {
X		    fprintf (stderr, "Reseting all drives\n");
X		    for (drive = 0; drive <= NUM_DRIVES; drive++)
X			allocated[drive] = TRUE;
X		}
X		else {
X		    if (!isdigit (optarg[0])) {
X			fprintf (stderr,
X				"The -r option expects a numeric argument in the range (0,%d)%s",
X				NUM_DRIVES - 1, "\n\tor the string all.\n");
X			exit (13);
X		    }
X		    num_optarg = atoi (optarg);
X		    if (num_optarg < 0 || num_optarg > NUM_DRIVES - 1) {
X			fprintf (stderr,
X				"%d is not a valid drive number, valid drive numbers are in the range (0,%d).\n",
X				num_optarg, NUM_DRIVES - 1);
X			exit (13);
X		    }
X		    allocated[0] = TRUE;
X		    allocated[num_optarg + 1] = TRUE;
X		}
X		break;
X	    case 'n': 
X		if (!strcmp (optarg, "all")) {
X		    fprintf (stderr, "Allocating all drives\n");
X		    num_drives = NUM_DRIVES;
X		}
X		else {
X		    if (!isdigit (optarg[0])) {
X			fprintf (stderr,
X				"The -n option expects a numeric argument in the range (1,%d)%s",
X				NUM_DRIVES, "\n\tor the string all.\n");
X			exit (13);
X		    }
X		    num_optarg = atoi (optarg);
X		    if (num_optarg < 1 || num_optarg > NUM_DRIVES) {
X			fprintf (stderr,
X				"%d is not a valid number of drives,\n\tvalid numbers of drives are in the range (1,%d).\n",
X				num_optarg, NUM_DRIVES);
X			exit (13);
X		    }
X		    num_drives = num_optarg;
X		}
X		break;
X	    case 'c': 
X		tape_command = optarg;
X		break;
X	    case 'v': 
X		verbose = !verbose;
X		break;
X	    case 't': 
X		if (!privileged_user ())
X		    fprintf (stderr,
X			    "You have to be root or belong the the group tapemgr to use the -t option.\n");
X		else
X		    t_out = !t_out;
X		break;
X	    case '?': 
X	    default: 
X		fprintf (stderr,
X			"Usage: %s [-r DRIVE_NUMBER|all] | [-n NUMBER_OF_DRIVES|all]%s",
X			called, "\n\t[-c TAPE_COMMAND] [-v] [-t]\n");
X		exit (13);
X		break;
X	}
X    if (!allocated[0]) {	/* oh boy, we get some drives */
X	(void) sigvec (SIGINT, &vec_clean_up, &ovec);
X				/* clean up after ^C */
X	org_sigmask = sigsetmask (~(1 << (SIGINT - 1)));
X	if (verbose)
X	    printf ("Attempting to allocate %d drives.\n", num_drives);
X	for (num_allocated = 0; num_allocated < num_drives;) {
X	    for (drive = 1, one_allocated = FALSE;
X		    drive <= NUM_DRIVES && !one_allocated; drive++) {
X		if (allocated[drive])
X		    continue;
X		(void) sprintf (cur_drive,
X			"%s%d", RMT_NAME, NAR_800BPI (drive));
X		if (stat (cur_drive, &drive_stat)) {
X		    perror (cur_drive);
X		    continue;
X		}
X		if (drive_stat.st_uid != TAPEMGR_UID)
X		    continue;
X		allocated[drive] = TRUE;
X		fprintf (stderr, "Allocating drive %d.\n", drive - 1);
X		set_device (cur_drive, getuid (), TRUE, FALSE);
X		sleep (SLEEP1);
X		(void) stat (cur_drive, &drive_stat);
X		if (drive_stat.st_uid != getuid ()) {
X		    allocated[drive] = FALSE;
X		    fprintf (stderr,
X			    "Thought we had drive %d but I guess I was wrong.\n",
X			    drive - 1);
X		    continue;
X		}
X		set_drive (drive, getuid ());
X		set_env (drive - 1, envp);
X		one_allocated = TRUE;
X		num_allocated++;
X		logmesg ("Allocation", drive);
X	    }
X	    if (!one_allocated) {
X		if (retries > MAX_RETRIES) {
X		    fprintf (stderr,
X			    "Could not allocate the requested number of drives.\n");
X		    clean_up (NO_SIGNAL, NO_CODE, NO_CONTEXT);
X		}
X		else {
X		    if (verbose)
X			printf ("Retrying...snore for %d secs", SLEEP3);
X		    sleep (SLEEP3);
X		    if (verbose)
X			printf ("...yawn let's try again.\n");
X		    retries++;
X		}
X	    }
X	}
X	(void) sigsetmask (~0);
X	if (isatty (fileno (stderr)))/* are we attached to a terminal? */
X	    fd_tty = fileno (stderr);
X	else
X	    if (isatty (fileno (stdout)))
X		fd_tty = fileno (stdout);
X	    else
X		if (isatty (fileno (stdin)))
X		    fd_tty = fileno (stdin);
X	if (fd_tty != EOF) {
X	    (void) ioctl (fd_tty, (int) TIOCGPGRP, (char *) & otpgrp);
X#ifdef RESTORE_TERMINAL
X	    (void) ioctl (fd_tty, (int) TIOCGETD, (char *) & oldisc);
X	    (void) ioctl (fd_tty, (int) TIOCGETP, (char *) & otty);
X	    (void) ioctl (fd_tty, (int) TIOCGETC, (char *) & otchars);
X#ifdef KERNAL_PAGER
X	    (void) ioctl (fd_tty, (int) TIOCGPAG, (char *) & otpage);
X#endif KERNAL_PAGER
X	    (void) ioctl (fd_tty, (int) TIOCLGET, (char *) & olocalm);
X	    (void) ioctl (fd_tty, (int) TIOCGLTC, (char *) & oltchars);
X#endif RESTORE_TERMINAL
X	}
X	if ((pid = fork ()) == EOF)
X	    perror (called);
X	else
X	    if (pid) {
X		if (t_out) {
X		    time_out.it_value.tv_sec = TIME_OUT;
X		    time_out.it_interval.tv_sec = WARNING;
X		    (void) sigvec (SIGALRM, &vec_timeout, &ovec);
X		    sleep (SLEEP1);
X		    if (verbose)
X			printf ("Timeout in %d hours %d minutes %d seconds.\n",
X				(int) TIME_OUT / 3600,
X				(int) (TIME_OUT % 3600) / 60, TIME_OUT % 60);
X		    (void) setitimer (ITIMER_REAL, &time_out, &ovalue);
X		}
X		(void) sigvec (SIGHUP, &vec_kill_and_clean, &ovec);
X		(void) sigsetmask (~((1 << (SIGHUP - 1)) |
X			    ((t_out ? 1 : 0) << (SIGALRM - 1))));
X		(void) wait (&status);
X		pid = 0;
X		if (t_out) {
X		    (void) setitimer (ITIMER_REAL, &ovalue, &time_out);
X		    (void) sigvec (SIGALRM, &vec_dfl, &ovec);
X		}
X	    }
X	    else {
X		if (verbose)
X		    printf ("Starting up: `%s'\n", tape_command);
X		(void) setuid (getuid ());
X		(void) sigsetmask (org_sigmask);
X		(void) sigblock (1 << (SIGINT - 1));
X		(void) sigblock (1 << (SIGQUIT - 1));
X		(void) sigblock (1 << (SIGTSTP - 1));
X		(void) execle (SHELL, SHELL, "-c", tape_command, 0,
X			tapemgr_env);
X		perror (tape_command);
X		exit (13);
X	    }
X    }
X    clean_up (NO_SIGNAL, NO_CODE, NO_CONTEXT);
X}
SHAR_EOF
if test 24155 -ne "`wc -c 'tapemgr.c'`"
then
	echo shar: error transmitting "'tapemgr.c'" '(should have been 24155 characters)'
fi
#	End of shell archive
exit 0
-- 

			     Michael Thompson
CDN:			   thompson@cs.dal.ean
UUCP:	     ...{utcsri,garfield,dartvax}!thompson@dalcs.uucp