[comp.unix.admin] Wanted -- tape management system

russell@ccu1.aukuni.ac.nz (Russell J Fulton;ccc032u) (12/06/90)

We need a system to manage tapes on our SGI 4D system. We are looking for the
following features.

    1/ supports ANSI labels
    2/ mount and unmount operations for both labeled and unlabeled tapes.
    3/ catalog of tape giving storage location and other information for 
       tape held by the system.
    4/ wrappers for tar etc to read and then skip over labels.

Item 2 implies managing the /dev... files to maintain secure access to who
ever has the device mounted at any particular time.

I would be interested in system that does parts of this e.g. manage the
security of the /dev... files to prevent one user overwriting another's tape
since I will probably end up writing a system if I can't find one.
(I have done it before on an IBM VM system in PLI and assembler :-( )

Cheers Russell.


-- 
Russell Fulton, Computer Center, University of Auckland, New Zealand.
<rj_fulton@aukuni.ac.nz>

mcf@statware.UUCP (Mathieu Federspiel) (01/01/91)

The following programs handle the /dev files, by setting their
permissions so that only the user who locks the tape drive has rw
permissions.  You will have to do some setup of files.


#---------------------------------- cut here ----------------------------------
# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by mcf at statware on Thu Aug 17 15:42:15 1989
#
# This archive contains:
#	tmount.c	tumount.c	tmount.1	
#
# Error checking via wc(1) will be performed.

LANG=""; export LANG

echo x - tmount.c
cat >tmount.c <<'@EOF'
/*
   tmount: mount (reserve) tape drive for user

   By:  Mathieu Federspiel, mcf@statware
   October 1987
*/

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <pwd.h>
#define LOCKF "/usr/spool/uucp/LCK..mt\0"
#define DEVF "/dev/MT\0"
#define MAXDEVS 20
void exit();
char *progname;

main(argc, argv)
   int argc;
   char *argv[];
{
   FILE *flock, *efopen();
   int tmpi, ndevs, errflg=0;
   char *devname[MAXDEVS], *DEVFILE, *LOCKFILE;
   char *tapenr="1";
   int thisuser, thisgroup;
   struct stat statbuf;
   struct passwd *getpwuid(), pwbuf;
   extern int optind;
   extern char *optarg;

/* User help section */

   progname=argv[0];
   /* get options */
   while ((tmpi = getopt(argc, argv, "t:")) != EOF)
      switch (tmpi) {
         case 't':
            tapenr = optarg;
            break;
         case '?':
            errflg++;
      }
   if (errflg) {
      (void)printf("Usage: tmount [-t tape_number]\n");
      exit(1);
   }
   /* build DEVFILE and LOCKFILE from tapenr; default 1 */
   if ((DEVFILE=(char *)malloc((unsigned)50)) == NULL) {
      (void)printf("Malloc failed: DEVFILE\n");
      exit(1);
   }
   if ((LOCKFILE=(char *)malloc((unsigned)50)) == NULL) {
      (void)printf("Malloc failed: LOCKFILE\n");
      exit(1);
   }
   DEVFILE=strcat(DEVF,tapenr);
   LOCKFILE=strcat(LOCKF,tapenr);
   /* allocate space for device file names and get them */
   for (tmpi=0;tmpi<MAXDEVS;tmpi++) {
      if ((devname[tmpi]=(char *)malloc((unsigned)50)) == NULL) {
         (void)printf("Malloc failed: tmpi = %d\n",tmpi);
         exit(1);
      }
   }
   if ((ndevs=getdevs(devname,DEVFILE)) == 0) {
      (void)printf("Getdevs failed.\n");
      exit(1);
   }

/* Set UID */

   thisuser=getuid();
   thisgroup=getgid();
   if ((tmpi=setuid(0)) != 0) {
      (void)printf("Set UID failed.\n");
      exit(tmpi);
   }

/* Check lock file, set or exit */

   if (stat(LOCKFILE,&statbuf) != 0) {	/* failed, test why */
      if (errno == 2) {
         flock=efopen(LOCKFILE,"w");
         (void)fclose(flock);
         if (chown(LOCKFILE,thisuser,thisgroup) != 0) {
            (void)printf("Lock chown failed, errno = %d.\n",errno);
            exit(1);
         }
         if (chmod(LOCKFILE,0644) != 0) {
            (void)printf("Lock chmod failed, errno = %d.\n",errno);
            exit(1);
         }
         (void)printf("Lock file created; tape drive mounted.\n");
         for (tmpi=0;tmpi<ndevs;tmpi++) {
            if (chown(devname[tmpi],thisuser,thisgroup) != 0) {
               (void)printf("Dev chown failed, errno = %d.\n",errno);
               exit(1);
            }
         }
      } else {
         (void)printf("Stat failed, errno = %d.\n",errno);
         exit(1);
      }
   } else {				/* passed, file must exist */
      pwbuf = *getpwuid(statbuf.st_uid);
      (void)printf("Tape drive mounted for uid %d, %s.\n",
         statbuf.st_uid, pwbuf.pw_name);
      exit(1);
   }

   exit(0);
}

FILE *efopen(file, mode)	/* fopen file, die if cannot */
   char *file, *mode;
{
   FILE *fp, *fopen();
   extern char *progname;

   if ((fp = fopen(file, mode)) != NULL)
      return fp;
   (void)fprintf(stderr, "%s: can't open file %s mode %s\n",
      progname, file, mode);
   exit(1);
}

int getdevs(array,namefile)	/* get device file names */
   char *array[MAXDEVS], *namefile;
{
   FILE *fdevs, *efopen();
   int tmpi, nullnl(), nfiles=0;

   fdevs = efopen(namefile,"r");
   for (tmpi=0;tmpi<MAXDEVS;tmpi++) {
      if ((array[tmpi]=fgets(array[tmpi],50,fdevs)) != NULL) {
         (void)nullnl(array[tmpi]);
         nfiles++;
      } else {
         tmpi=MAXDEVS+1;
      }
   }
   if (tmpi == MAXDEVS) (void)printf("Warning: max device files reached.\n");
   if (fclose(fdevs) != 0) {
      (void)printf("Fclose on dev file failed.");
      exit(1);
   }
   return nfiles;
}

int nullnl(ptr)			/* null new-line characters */
   char *ptr;
{
   int tmpi=0;

   while (*ptr != '\0') {
      if (*ptr == '\n') {
         *ptr='\0';
         tmpi++;
      }
      ptr++;
   }
   return tmpi;
}
@EOF
if test "`wc -lwc <tmount.c`" != '    166    446   4109'
then
	echo ERROR: wc results of tmount.c are `wc -lwc <tmount.c` should be     166    446   4109
fi

chmod 644 tmount.c

echo x - tumount.c
cat >tumount.c <<'@EOF'
/*
   tumount: unmount (free) tape drive for other users

   By:  Mathieu Federspiel, mcf@statware
   October 1987
*/

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mtio.h>
#include <errno.h>
#include <string.h>
#include <pwd.h>
#include <fcntl.h>
#define LOCKF "/usr/spool/uucp/LCK..mt\0"
#define DEVF "/dev/MT\0"
#define MAXDEVS 20
void exit();
char *progname;

main(argc, argv)
   int argc;
   char *argv[];
{
   FILE *efopen();
   int open(), close(), fd;
   int tmpi, ndevs, errflg=0, lineopt=0;
   char *devname[MAXDEVS], *DEVFILE, *LOCKFILE;
   char *tapenr="1";
   int thisuser, thisgroup;
   struct stat statbuf;
   struct mtop top;
   struct passwd *getpwuid(), pwbuf;
   extern int optind;
   extern char *optarg;

/* User help section */

   progname=argv[0];
   /* get options */
   while ((tmpi = getopt(argc, argv, "t:l")) != EOF)
      switch (tmpi) {
         case 't':
            tapenr = optarg;
            break;
         case 'l':
            lineopt++;
            break;
         case '?':
            errflg++;
      }
   if (errflg) {
      (void)printf("Usage: tumount [-t tape_number] [-l]\n");
      exit(1);
   }
   /* build DEVFILE and LOCKFILE from tapenr; default 1 */
   if ((DEVFILE=(char *)malloc((unsigned)50)) == NULL) {
      (void)printf("Malloc failed: DEVFILE\n");
      exit(1);
   }
   if ((LOCKFILE=(char *)malloc((unsigned)50)) == NULL) {
      (void)printf("Malloc failed: LOCKFILE\n");
      exit(1);
   }
   DEVFILE=strcat(DEVF,tapenr);
   LOCKFILE=strcat(LOCKF,tapenr);
   /* allocate space for device file names and get them */
   for (tmpi=0;tmpi<MAXDEVS;tmpi++) {
      if ((devname[tmpi]=(char *)malloc((unsigned)50)) == NULL) {
         (void)printf("Malloc failed: tmpi = %d\n",tmpi);
         exit(1);
      }
   }
   if ((ndevs=getdevs(devname,DEVFILE)) == 0) {
      (void)printf("Getdevs failed.\n");
      exit(1);
   }

/* Get and Set UID */

   thisuser=getuid();
   thisgroup=getgid();
   if ((tmpi=setuid(0)) != 0) {
      (void)printf("Set UID failed.\n");
      exit(tmpi);
   }

/* Check lock file, set or exit */

   if (stat(LOCKFILE,&statbuf) != 0) {	/* failed, test why */
      if (errno == 2) {
         (void)printf("Tape drive not mounted.\n");
         exit(1);
      } else {
         (void)printf("Stat failed, errno = %d\n",errno);
         exit(1);
      }

   } else {				/* passed, file must exist */
      if (statbuf.st_uid == thisuser) {

		/* open mag tape file, take off line */
         if (! lineopt) {	/* option -l not specified */
            fd = open(devname[1],O_WRONLY);
            if (fd == -1) {
               if (errno != 5) {
                  (void)printf("Device open() failed, errno = %d.\n",errno);
                  exit(1);
               }
            }
            /* if fd == -1, I/O error, possibly tape off line.  Go on! */
            if (fd != -1) {
               top.mt_count = 1;
               top.mt_op = MTOFFL;
               fd = ioctl(fd,MTIOCTOP,&top);
            /* if failed, go on; may not be tape device
               if (fd == -1) {
                  (void)printf("Ioctl failed, errno = %d.\n",errno);
                  exit(1);
               }
            */
               fd = close(fd);
               if (fd == -1) {
                  (void)printf("Device close() failed, errno = %d.\n",errno);
                  exit(1);
               }
            }
         }

		/* restore owner of tape files to root */
         for (tmpi=0;tmpi<ndevs;tmpi++) {
            if (chown(devname[tmpi],0,thisgroup) != 0) {
               (void)printf("Chown failed, errno = %d.\n",errno);
               exit(1);
            }
         }

		/* remove lock file */
         if (unlink(LOCKFILE) != 0) {
            (void)printf("Unlink failed, errno = %d.\n",errno);
            exit(1);
         }
         (void)printf("Lock file removed; tape drive unmounted.\n");

      } else {
         pwbuf = *getpwuid(statbuf.st_uid);
         (void)printf("Permission denied: owner is uid %d, %s.\n",
            statbuf.st_uid, pwbuf.pw_name);
         exit(1);
      }
   }

   exit(0);
}

FILE *efopen(file, mode)	/* fopen file, die if cannot */
   char *file, *mode;
{
   FILE *fp, *fopen();
   extern char *progname;

   if ((fp = fopen(file, mode)) != NULL)
      return fp;
   (void)fprintf(stderr, "%s: can't open file %s mode %s\n",
      progname, file, mode);
   exit(1);
}

int getdevs(array,namefile)	/* get device file names */
   char *array[MAXDEVS], *namefile;
{
   FILE *fdevs, *efopen();
   int tmpi, nullnl(), nfiles=0;

   fdevs = efopen(namefile,"r");
   for (tmpi=0;tmpi<MAXDEVS;tmpi++) {
      if ((array[tmpi]=fgets(array[tmpi],50,fdevs)) != NULL) {
         (void)nullnl(array[tmpi]);
         nfiles++;
      } else {
         tmpi=MAXDEVS+1;
      }
   }
   if (tmpi == MAXDEVS) (void)printf("Warning: max device files reached.\n");
   if (fclose(fdevs) != 0) {
      (void)printf("Fclose on dev file failed.");
      exit(1);
   }
   return nfiles;
}

int nullnl(ptr)			/* null new-line characters */
   char *ptr;
{
   int tmpi=0;

   while (*ptr != '\0') {
      if (*ptr == '\n') {
         *ptr='\0';
         tmpi++;
      }
      ptr++;
   }
   return tmpi;
}
@EOF
if test "`wc -lwc <tumount.c`" != '    206    586   5244'
then
	echo ERROR: wc results of tumount.c are `wc -lwc <tumount.c` should be     206    586   5244
fi

chmod 644 tumount.c

echo x - tmount.1
cat >tmount.1 <<'@EOF'
.TH TMOUNT,TUMOUNT 1 "LOCAL"
.SH NAME
tmount, tumount \-
mount and umount tape drives

.SH SYNOPSIS
.B tmount
[ -t drive_number ]

.B tumount
[ -t drive_number ] [ -l ]

.SH HP-UX COMPATIBILITY
.TP 10
Level:
HP-UX/STANDARD
.TP
Origin:
Statware

.SH DESCRIPTION
These two utilities reserve or free tape drives for a
single user.
This prevents one user from destroying another's tape which may
be mounted at the time.

The tape drive is reserved by permitting read and write to the tape's
device files only by the owner of the device.
The owner is set by
.BR tmount ,
and set back to root by
.BR tumount .
In addition, a lock file is created which will provide information
to others about who is using the tape drive.

For the tape drive, \fIall\fR device files which access this device
are modified to have the current user as owner with no access permission
for other users.
The names of all device files are maintained in /dev/MT*.
By default, the file /dev/MT1 is read.
The option \fI-t\fR
may be used to specify another tape drive.
For example,
.B tmount -t2
is used to reserve the tape drive defined by the device files
listed in /dev/MT2.
It is up to the system administrator to enter the correct
device files in the appropriate /dev/MT* file.

Note that as this system depends upon the access permissions
of the device files, any device may be reserved by using
tmount and tumount.
The system administrator may define any number of /dev/MT*
files to control any number of devices.

Tumount will attempt to take the tape drive off line, using
.BR ioctl(2) .
This is disabled with \fI-l\fR.

.SH FILES
.nf
/usr/spool/uucp/LCK..mt*
/dev/MT*
.fi

.SH ERROR MESSAGES
Tmount and tumount return 0 on successful completion, 1
otherwise.

.TP
Set UID failed.
The SUID bit is not set; owner must be root.
.TP
Tape drive not mounted.
For tumount, drive is not mounted; i.e., tmount has not been used
to reserve the tape drive.
.TP
Permission denied: owner is uid, uname.
Tape is mounted for another user.
This error will be issued by both tmount and tumount if tmount has
been used to reserve the tape drive.
.TP
Other errors:
Indicate invalid device file specified in /dev/MT* or other
invalid access to a file.
See the system administrator for proper setup of file
permissions.
@EOF
if test "`wc -lwc <tmount.1`" != '     87    396   2275'
then
	echo ERROR: wc results of tmount.1 are `wc -lwc <tmount.1` should be      87    396   2275
fi

chmod 664 tmount.1

exit 0
-- 
Mathieu Federspiel                  mcf%statware.uucp@cs.orst.edu
Statware                            orstcs!statware!mcf
260 SW Madison Avenue, Suite 109    503-753-5382
Corvallis  OR  97333  USA           503-758-4666 FAX