/* @(#)mt.c 1.19 03/02/07 Copyright 2000 J. Schilling */
#ifndef lint
static char sccsid[] =
"@(#)mt.c 1.19 03/02/07 Copyright 2000 J. Schilling";
#endif
/*
* Magnetic tape manipulation program
*
* Copyright (c) 2000 J. Schilling
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; see the file COPYING. If not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <mconfig.h>
/*
* XXX Until we find a better way, the next definitions must be in sync
* XXX with the definitions in librmt/remote.c
*/
#if !defined(HAVE_FORK) || !defined(HAVE_SOCKETPAIR) || !defined(HAVE_DUP2)
#undef USE_RCMD_RSH
#endif
#if !defined(HAVE_GETSERVBYNAME)
#undef USE_REMOTE /* Cannot get rcmd() port # */
#endif
#if (!defined(HAVE_NETDB_H) || !defined(HAVE_RCMD)) && !defined(USE_RCMD_RSH)
#undef USE_REMOTE /* There is no rcmd() */
#endif
#include <stdio.h>
#include <stdxlib.h>
#include <unixstd.h>
#include <strdefs.h>
#include <utypes.h>
#include <fctldefs.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <schily.h>
#include <standard.h>
/*#undef HAVE_SYS_MTIO_H*/
#include <mtiodefs.h>
#include <librmt.h>
LOCAL BOOL help;
LOCAL BOOL prvers;
LOCAL BOOL wready;
LOCAL int debug;
LOCAL struct mtop mt_op;
LOCAL struct rmtget mt_status;
#ifndef HAVE_MTGET_TYPE
#ifdef HAVE_MTGET_MODEL
#define HAVE_MTGET_TYPE
#define mt_type mt_model
#endif
#endif
#define NO_ASF 1000
#define NO_NBSF 1001
#ifndef MTASF
# define MTASF NO_ASF
#endif
#ifndef MTNBSF
# define MTNBSF NO_NBSF
#endif
#define MTC_NONE 0 /* No flags defined */
#define MTC_RW 0 /* This command writes to the tape */
#define MTC_RDO 1 /* This command does not write */
#define MTC_CNT 2 /* This command uses the count arg */
LOCAL struct mt_cmds {
char *mtc_name; /* The name of the command */
char *mtc_text; /* Description of the command */
int mtc_opcode; /* The opcode for mtio */
int mtc_flags; /* Flags for this command */
} cmds[] = {
#ifdef MTWEOF
{ "weof", "write EOF mark", MTWEOF, MTC_RW|MTC_CNT },
{ "eof", "write EOF mark", MTWEOF, MTC_RW|MTC_CNT },
#endif
#ifdef MTFSF
{ "fsf", "forward skip FILE mark", MTFSF, MTC_RDO|MTC_CNT },
#endif
#ifdef MTBSF
{ "bsf", "backward skip FILE mark", MTBSF, MTC_RDO|MTC_CNT },
#endif
{ "asf", "absolute FILE mark pos", MTASF, MTC_RDO|MTC_CNT },
#ifdef MTFSR
{ "fsr", "forward skip record", MTFSR, MTC_RDO|MTC_CNT },
#endif
#ifdef MTBSR
{ "bsr", "backward skip record", MTBSR, MTC_RDO|MTC_CNT },
#endif
#ifdef MTREW
{ "rewind", "rewind tape", MTREW, MTC_RDO },
#endif
#ifdef MTOFFL
{ "offline", "rewind and unload", MTOFFL, MTC_RDO },
{ "rewoffl", "rewind and unload", MTOFFL, MTC_RDO },
#endif
#ifdef MTNOP
{ "status", "get tape status", MTNOP, MTC_RDO },
#endif
{ "nop", "no operation", MTNOP, MTC_RDO },
#ifdef MTRETEN
{ "retension", "retension tape cartridge", MTRETEN, MTC_RDO },
#endif
#ifdef MTERASE
{ "erase", "erase tape", MTERASE, MTC_RW },
#endif
#ifdef MTEOM
{ "eom", "position to EOM", MTEOM, MTC_RDO },
#endif
#if MTNBSF != NO_NBSF
{ "nbsf", "backward skip FILE mark", MTNBSF, MTC_RDO|MTC_CNT },
#endif
#ifdef MTLOAD
{ "load", "load tape", MTLOAD, MTC_RDO },
#endif
{ NULL, NULL, 0, MTC_NONE }
};
LOCAL void usage __PR((int ex));
EXPORT int main __PR((int ac, char *av[]));
LOCAL void mtstatus __PR((struct rmtget *sp));
LOCAL char *print_key __PR((Llong key));
LOCAL int openremote __PR((char *tape));
LOCAL int opentape __PR((char *tape, struct mt_cmds *cp));
LOCAL int mtioctl __PR((int cmd, caddr_t arg));
LOCAL void
usage(ex)
int ex;
{
struct mt_cmds *cp;
int i;
error("Usage: mt [ -f device ] [options] command [ count ]\n");
error("Options:\n");
error("\t-help\t\tprint this online help\n");
error("\t-version\tprint version number\n");
error("\t-wready\t\twait for the tape to become ready before doing command\n");
error("\n");
error("Commands are:\n");
for (cp = cmds; cp->mtc_name != NULL; cp++) {
error("%s%n", cp->mtc_name, &i);
error("%*s%s\n", 14-i, "", cp->mtc_text);
}
exit(ex);
}
LOCAL char opts[] = "f*,t*,version,help,h,debug,wready";
int
main(ac, av)
int ac;
char *av[];
{
int cac;
char * const *cav;
char *tape = NULL;
char *cmd = "BADCMD";
int count = 1;
struct mt_cmds *cp;
save_args(ac, av);
cac = --ac;
cav = ++av;
if (getallargs(&cac, &cav, opts,
&tape, &tape,
&prvers,
&help, &help,
&debug,
&wready) < 0) {
errmsgno(EX_BAD, "Bad Option: '%s'.\n", cav[0]);
usage(EX_BAD);
}
if (help) usage(0);
if (prvers) {
printf("mt %s (%s-%s-%s)\n\n", "1.19", HOST_CPU, HOST_VENDOR, HOST_OS);
printf("Copyright (C) 2000 Jörg Schilling\n");
printf("This is free software; see the source for copying conditions. There is NO\n");
printf("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
exit(0);
}
if (tape == NULL && (tape = getenv("TAPE")) == NULL) {
#ifdef DEFTAPE
tape = DEFTAPE;
#else
errmsgno(EX_BAD, "No default tape defined.\n");
usage(EX_BAD);
/* NOTREACHED */
#endif
}
cac = ac;
cav = av;
if (getfiles(&cac, &cav, opts) == 0) {
errmsgno(EX_BAD, "Missing args.\n");
usage(EX_BAD);
} else {
cmd = cav[0];
cav++;
cac--;
}
if (getfiles(&cac, &cav, opts) > 0) {
if (*astoi(cav[0], &count) != '\0') {
errmsgno(EX_BAD, "Not a number: '%s'.\n", cav[0]);
usage(EX_BAD);
}
if (count < 0) {
comerrno(EX_BAD, "negative file number or repeat count\n");
/* NOTREACHED */
}
cav++;
cac--;
}
if (getfiles(&cac, &cav, opts) > 0) {
errmsgno(EX_BAD, "Too many args.\n");
usage(EX_BAD);
}
for (cp = cmds; cp->mtc_name != NULL; cp++) {
if (strncmp(cmd, cp->mtc_name, strlen(cmd)) == 0)
break;
}
if (cp->mtc_name == NULL) {
comerrno(EX_BAD, "Unknown command: %s\n", cmd);
/* NOTREACHED */
}
#ifdef DEBUG
error("cmd: %s opcode: %d %s %s\n",
cp->mtc_name, cp->mtc_opcode,
(cp->mtc_flags & MTC_RDO) != 0 ? "RO":"RW",
(cp->mtc_flags & MTC_CNT) != 0 ? "usecount":"");
#endif
if ((cp->mtc_flags & MTC_CNT) == 0)
count = 1;
#ifdef USE_REMOTE
rmtdebug(debug);
(void)openremote(tape); /* This needs super user privilleges */
#endif
#ifdef HAVE_SETREUID
if (setreuid(-1, getuid()) < 0)
#else
#ifdef HAVE_SETEUID
if (seteuid(getuid()) < 0)
#else
if (setuid(getuid()) < 0)
#endif
#endif
comerr("Panic cannot set back effective uid.\n");
if (opentape(tape, cp) < 0) {
if (geterrno() == EIO) {
comerrno(EX_BAD, "'%s': no tape loaded or drive offline.\n",
tape);
} else if (geterrno() == EACCES) {
comerrno(EX_BAD, "'%s': tape is write protected.\n", tape);
} else {
comerr("Cannot open '%s'.\n", tape);
}
/* NOTREACHED */
}
#ifdef DEBUG
error("Tape: %s cmd : %s (%s) count: %d\n", tape, cmd, cp->mtc_name, count);
#endif
if (cp->mtc_opcode == MTNOP) {
if (strcmp(cp->mtc_name, "nop")) {
/*
* Status ioctl
*/
if (mtioctl(MTIOCGET, (caddr_t)&mt_status) < 0) {
comerr("Cannot get mt status from '%s'.\n", tape);
/* NOTREACHED */
}
mtstatus(&mt_status);
}
#if MTASF == NO_ASF
} else if (cp->mtc_opcode == MTASF) {
(void)mtioctl(MTIOCGET, (caddr_t)&mt_status);
if (mtioctl(MTIOCGET, (caddr_t)&mt_status) < 0) {
comerr("Cannot get mt status from '%s'.\n", tape);
/* NOTREACHED */
}
/*
* If the device does not support to report the current file
* tape file position - rewind the tape, and space forward.
*/
#ifndef MTF_ASF
if (1) {
#else
if (!(mt_status.mt_flags & MTF_ASF) || MTNBSF == NO_NBSF) {
#endif
mt_status.mt_fileno = 0;
mt_op.mt_count = 1;
mt_op.mt_op = MTREW;
if (mtioctl(MTIOCTOP, (caddr_t)&mt_op) < 0) {
comerr("%s %s %d failed\n", tape, cp->mtc_name,
count);
/* NOTREACHED */
}
}
if (count < mt_status.mt_fileno) {
mt_op.mt_op = MTNBSF;
mt_op.mt_count = mt_status.mt_fileno - count;
/*printf("mt: bsf= %lld\n", (Llong)mt_op.mt_count);*/
} else {
mt_op.mt_op = MTFSF;
mt_op.mt_count = count - mt_status.mt_fileno;
/*printf("mt: fsf= %lld\n", (Llong)mt_op.mt_count);*/
}
if (mtioctl(MTIOCTOP, (caddr_t)&mt_op) < 0) {
if (mtioctl(MTIOCTOP, (caddr_t)&mt_op) < 0) {
comerr("%s %s %d failed\n", tape, cp->mtc_name,
count);
/* NOTREACHED */
}
}
#endif
} else {
/*
* Regular magnetic tape ioctl
*/
mt_op.mt_op = cp->mtc_opcode;
mt_op.mt_count = count;
if (mtioctl(MTIOCTOP, (caddr_t)&mt_op) < 0) {
comerr("%s %s %lld failed\n", tape, cp->mtc_name,
(Llong)mt_op.mt_count);
/* NOTREACHED */
}
}
return (0);
}
/*
* If we try to make this portable, we need a way to initialize it
* in an OS independant way.
* Don't use it for now.
*/
LOCAL struct tape_info {
short t_type; /* type of magnetic tape device */
char *t_name; /* name for prining */
char *t_dsbits; /* "drive status" register */
char *t_erbits; /* "error" register */
} tapes[] = {
#ifdef XXX
{ MT_ISTS, "ts11", 0, TSXS0_BITS },
#endif
{ 0 }
};
/*
* Interpret the status buffer returned
*/
LOCAL void
mtstatus(sp)
register struct rmtget *sp;
{
register struct tape_info *mt = NULL;
#ifdef XXX
#ifdef HAVE_MTGET_TYPE
for (mt = tapes; mt->t_type; mt++)
if (mt->t_type == sp->mt_type)
break;
#endif
#endif
#if defined(MTF_SCSI)
if ((sp->mt_xflags & RMT_FLAGS) && (sp->mt_flags & MTF_SCSI)) {
/*
* Handle SCSI tape drives specially.
*/
if (sp->mt_xflags & RMT_TYPE) {
if (mt != NULL && mt->t_type == sp->mt_type)
printf("%s tape drive:\n", mt->t_name);
else
printf("%s tape drive:\n", "SCSI");
} else {
printf("Unknown SCSI tape drive:\n");
}
printf(" sense key(0x%llx)= %s residual= %lld ",
sp->mt_erreg, print_key(sp->mt_erreg), sp->mt_resid);
printf("retries= %lld\n", sp->mt_dsreg);
} else
#endif /* MTF_SCSI */
{
/*
* Handle other drives below.
*/
if (sp->mt_xflags & RMT_TYPE) {
if (mt == NULL || mt->t_type == 0) {
printf("Unknown tape drive type (0x%llX):\n", (Ullong)sp->mt_type);
} else {
printf("%s tape drive:\n", mt->t_name);
}
} else {
printf("Unknown tape drive:\n");
}
if (sp->mt_xflags & RMT_RESID)
printf(" residual= %lld", sp->mt_resid);
/*
* If we implement better support for specific OS,
* then we may want to implement something like the
* *BSD kernel %b printf format (e.g. printreg).
*/
if (sp->mt_xflags & RMT_DSREG)
printf (" ds = %llX", (Ullong)sp->mt_dsreg);
if (sp->mt_xflags & RMT_ERREG)
printf (" er = %llX", sp->mt_erreg);
putchar('\n');
}
printf(" file no= %lld block no= %lld\n",
(sp->mt_xflags & RMT_FILENO)?
sp->mt_fileno:
(Llong)-1,
(sp->mt_xflags & RMT_BLKNO)?
sp->mt_blkno:
(Llong)-1);
if (sp->mt_xflags & RMT_BF)
printf(" optimum blocking factor= %ld\n", sp->mt_bf);
if (sp->mt_xflags & RMT_FLAGS)
printf(" flags= 0x%llX\n", sp->mt_flags);
}
static char *sense_keys[] = {
"No Additional Sense", /* 0x00 */
"Recovered Error", /* 0x01 */
"Not Ready", /* 0x02 */
"Medium Error", /* 0x03 */
"Hardware Error", /* 0x04 */
"Illegal Request", /* 0x05 */
"Unit Attention", /* 0x06 */
"Data Protect", /* 0x07 */
"Blank Check", /* 0x08 */
"Vendor Unique", /* 0x09 */
"Copy Aborted", /* 0x0a */
"Aborted Command", /* 0x0b */
"Equal", /* 0x0c */
"Volume Overflow", /* 0x0d */
"Miscompare", /* 0x0e */
"Reserved" /* 0x0f */
};
LOCAL char *
print_key(key)
Llong key;
{
static char keys[32];
if (key >= 0 && key < (sizeof(sense_keys)/sizeof(sense_keys[0])))
return (sense_keys[key]);
js_snprintf(keys, sizeof(keys), "Unknown Key: %lld", key);
return (keys);
}
/*--------------------------------------------------------------------------*/
LOCAL int isremote;
LOCAL int remfd = -1;
LOCAL int mtfd;
LOCAL char *remfn;
#ifdef USE_REMOTE
LOCAL int
openremote(tape)
char *tape;
{
char host[128];
if ((remfn = rmtfilename(tape)) != NULL) {
rmthostname(host, sizeof(host), tape);
isremote++;
if (debug)
errmsgno(EX_BAD, "Remote: %s Host: %s file: %s\n",
tape, host, remfn);
if ((remfd = rmtgetconn(host, 4096, 0)) < 0)
comerrno(EX_BAD, "Cannot get connection to '%s'.\n",
/* errno not valid !! */ host);
}
return (isremote);
}
#endif
LOCAL int
opentape(tape, cp)
char *tape;
register struct mt_cmds *cp;
{
int ret;
int n = 0;
retry:
ret = 0;
if (isremote) {
#ifdef USE_REMOTE
if (rmtopen(remfd, remfn, (cp->mtc_flags&MTC_RDO) ? O_RDONLY : O_RDWR) < 0)
ret = -1;
#else
comerrno(EX_BAD, "Remote tape support not present.\n");
#endif
} else if ((mtfd = open(tape, (cp->mtc_flags&MTC_RDO) ? O_RDONLY : O_RDWR)) < 0) {
ret = -1;
}
if (wready && n++ < 120 && (geterrno() == EIO || geterrno() == EBUSY)) {
sleep(1);
goto retry;
}
return (ret);
}
LOCAL int
mtioctl(cmd, arg)
int cmd;
caddr_t arg;
{
int ret = -1;
struct rmtget *mtp;
struct mtop *mop;
if (isremote) {
#ifdef USE_REMOTE
switch (cmd) {
case MTIOCGET:
ret = rmtxstatus(remfd, (struct rmtget *)arg);
if (ret < 0)
return (ret);
mtp = (struct rmtget *)arg;
/*#define DEBUG*/
#ifdef DEBUG
error("type: %llX ds: %llX er: %llX resid: %lld fileno: %lld blkno: %lld flags: %llX bf: %ld\n",
mtp->mt_type, mtp->mt_dsreg, mtp->mt_erreg, mtp->mt_resid, mtp->mt_fileno,
mtp->mt_blkno, mtp->mt_flags, mtp->mt_bf);
#endif
break;
case MTIOCTOP:
mop = (struct mtop *)arg;
ret = rmtioctl(remfd, mop->mt_op, mop->mt_count);
break;
default:
comerrno(ENOTTY, "Invalid mtioctl.\n");
/* NOTREACHED */
}
#else
comerrno(EX_BAD, "Remote tape support not present.\n");
#endif
} else {
if (cmd == MTIOCGET) {
struct mtget mtget;
ret = ioctl(mtfd, cmd, &mtget);
if (ret >= 0) {
if (_mtg2rmtg((struct rmtget *)arg, &mtget) < 0)
ret = -1;
}
} else
ret = ioctl(mtfd, cmd, arg);
}
return (ret);
}
syntax highlighted by Code2HTML, v. 0.9.1