#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/mtio.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/buf.h>
#include <sys/conf.h>
#include <sys/file.h>
#include <sys/uio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/scsi/targets/stdef.h>
#include <fcntl.h>
#define equal(s1, s2) (strcmp(s1, s2) == 0)
#define MTASF 100
#define DEFAULT_NRW_TAPE "/dev/rmt/0n"
static int print_config(int mtfd);
static char *print_key(short key_code);
static void printreg(char *, ushort_t, char *);
static int status(int mtfd, struct mtget *);
#define O_UNLOAD (O_RDWR | O_NDELAY)
static const struct commands {
char *c_name;
int c_code;
int c_oflag;
int c_usecnt;
} com[] = {
{ "weof", MTWEOF, O_RDWR, 1 },
{ "eof", MTWEOF, O_RDWR, 1 },
{ "fsf", MTFSF, O_RDONLY, 1 },
{ "bsf", MTBSF, O_RDONLY, 1 },
{ "asf", MTASF, O_RDONLY, 1 },
{ "fsr", MTFSR, O_RDONLY, 1 },
{ "bsr", MTBSR, O_RDONLY, 1 },
{ "rewind", MTREW, O_RDONLY, 0 },
{ "offline", MTOFFL, O_RDONLY, 0 },
{ "rewoffl", MTOFFL, O_RDONLY, 0 },
{ "status", MTNOP, O_RDONLY, 0 },
{ "retension", MTRETEN, O_RDONLY, 0 },
{ "erase", MTERASE, O_RDWR, 0 },
{ "eom", MTEOM, O_RDONLY, 0 },
{ "nbsf", MTNBSF, O_RDONLY, 1 },
{ "reserve", MTIOCRESERVE, O_RDONLY, 0 },
{ "release", MTIOCRELEASE, O_RDONLY, 0 },
{ "forcereserve", MTIOCFORCERESERVE, O_UNLOAD, 0 },
{ "config", MTIOCGETDRIVETYPE, O_UNLOAD, 0 },
{ "fssf", MTFSSF, O_RDONLY, 1 },
{ "bssf", MTBSSF, O_RDONLY, 1 },
{ "tell", MTTELL, O_RDONLY, 0 },
{ "seek", MTSEEK, O_RDONLY, 1 },
{ "load", MTLOAD, O_UNLOAD, 0 },
{ "lock", MTLOCK, O_RDONLY, 0 },
{ "unlock", MTUNLOCK, O_RDONLY, 0 },
{ 0 }
};
int
main(int argc, char **argv)
{
char *cp;
char *tape;
int mtfd;
struct commands const *comp;
struct mtget mt_status;
struct mtlop mt_com;
if (argc > 2 && (equal(argv[1], "-t") || equal(argv[1], "-f"))) {
argc -= 2;
tape = argv[2];
argv += 2;
} else {
tape = getenv("TAPE");
if (tape == NULL) {
tape = DEFAULT_NRW_TAPE;
}
}
if (argc < 2) {
(void) fprintf(stderr,
"usage: mt [ -f device ] command [ count ]\n");
return (1);
}
cp = argv[1];
for (comp = com; comp->c_name != NULL; comp++) {
if (strncmp(cp, comp->c_name, strlen(cp)) == 0) {
break;
}
}
if (comp->c_name == NULL) {
(void) fprintf(stderr, "mt: unknown command: %s\n", cp);
return (1);
}
mtfd = open(tape, comp->c_oflag);
if (mtfd < 0) {
if (errno == EIO) {
(void) fprintf(stderr,
"%s: no tape loaded or drive offline\n", tape);
} else if (errno == EACCES) {
(void) fprintf(stderr,
"%s: write protected or reserved.\n", tape);
} else {
perror(tape);
}
return (1);
}
if (comp->c_code == MTIOCFORCERESERVE ||
comp->c_code == MTIOCRESERVE ||
comp->c_code == MTIOCRELEASE) {
if (ioctl(mtfd, comp->c_code) < 0) {
perror("mt");
return (2);
}
} else if (comp->c_code == MTASF) {
int usecnt;
daddr_t mt_fileno;
usecnt = argc > 2 && comp->c_usecnt;
mt_fileno = usecnt ? atol(argv[2]) : 1;
if (mt_fileno < 0) {
(void) fprintf(stderr, "mt: negative file number\n");
return (1);
}
(void) ioctl(mtfd, MTIOCGET, (char *)&mt_status);
if (ioctl(mtfd, MTIOCGET, (char *)&mt_status) < 0) {
perror("mt");
return (2);
}
if ((mt_status.mt_flags & MTF_ASF) == 0 ||
(mt_status.mt_fileno == -1)) {
mt_com.mt_count = 1;
mt_com.mt_op = MTREW;
if (ioctl(mtfd, MTIOCLTOP, &mt_com) < 0) {
(void) fprintf(stderr, "%s %s %ld ",
tape, comp->c_name, mt_fileno);
perror("mt");
return (2);
}
mt_status.mt_fileno = 0;
mt_status.mt_blkno = 0;
}
if (mt_fileno < mt_status.mt_fileno) {
mt_com.mt_op = MTNBSF;
mt_com.mt_count = mt_status.mt_fileno - mt_fileno;
} else {
mt_com.mt_op = MTFSF;
mt_com.mt_count = mt_fileno - mt_status.mt_fileno;
}
if (ioctl(mtfd, MTIOCLTOP, &mt_com) < 0) {
(void) fprintf(stderr, "%s %s %ld ", tape, comp->c_name,
mt_fileno);
perror("failed");
return (2);
}
} else if (comp->c_code == MTIOCGETDRIVETYPE) {
return (print_config(mtfd));
} else if (comp->c_code != MTNOP) {
int usecnt;
mt_com.mt_op = comp->c_code;
usecnt = argc > 2 && comp->c_usecnt;
mt_com.mt_count = (usecnt ? atoll(argv[2]) : 1);
if (mt_com.mt_count < 0) {
(void) fprintf(stderr, "mt: negative %s count\n",
comp->c_name);
return (1);
}
if (ioctl(mtfd, MTIOCLTOP, &mt_com) < 0) {
if (mt_com.mt_op == MTTELL &&
comp->c_code == MTSEEK) {
(void) printf("partial seek:at block = %llu.\n",
mt_com.mt_count);
} else {
(void) fprintf(stderr, "%s %s %lld ", tape,
comp->c_name, mt_com.mt_count);
perror("failed");
}
return (2);
}
if (mt_com.mt_op == MTTELL) {
(void) printf("At block = %llu.\n", mt_com.mt_count);
}
} else {
if (ioctl(mtfd, MTIOCGET, (char *)&mt_status) < 0) {
perror("mt");
return (2);
}
return (status(mtfd, &mt_status));
}
return (0);
}
static int
print_config(int mtfd)
{
struct mtdrivetype mdt;
struct mtdrivetype_request mdt_req;
char cfgname[48];
char tmp[2];
char *name;
int i;
mdt_req.size = sizeof (mdt);
mdt_req.mtdtp = &mdt;
if (ioctl(mtfd, MTIOCGETDRIVETYPE, &mdt_req) != 0) {
perror("mt config");
return (2);
}
for (i = VIDPIDLEN; i; i--) {
if (isspace(mdt.vid[i]) || mdt.vid[i] == '*') {
mdt.vid[i] = 0;
} else if (mdt.vid[i] == 0) {
continue;
} else {
break;
}
}
if (strstr(mdt.name, "Vendor '") == NULL) {
name = mdt.name;
} else {
name = mdt.vid;
}
(void) strcpy(cfgname, "CFG");
for (tmp[1] = i = 0; i < VIDPIDLEN; i++) {
if (!isalnum(name[i]))
continue;
if (isspace(name[i]))
continue;
tmp[0] = toupper(name[i]);
(void) strncat(cfgname, tmp, 1);
}
(void) printf("\"%s\", \"%s\", \"%s\";\n", mdt.vid, name, cfgname);
(void) printf("%s = 2,0x%X,%d,0x%X,", cfgname,
mdt.type, mdt.bsize, mdt.options &
~(ST_DYNAMIC | ST_LONG_ERASE | ST_LONG_TIMEOUTS | ST_KNOWS_MEDIA));
(void) printf("4,0x%2.2X,0x%2.2X,0x%2.2X,0x%2.2X,%d,",
mdt.densities[0], mdt.densities[1], mdt.densities[2],
mdt.densities[3], mdt.default_density >> 3);
(void) printf("%d,%d,%d,%d,%d,%d,%d;\n", mdt.non_motion_timeout,
mdt.io_timeout, mdt.rewind_timeout, mdt.space_timeout,
mdt.load_timeout, mdt.unload_timeout, mdt.erase_timeout);
return (0);
}
static int
status(int mtfd, struct mtget *bp)
{
struct mtdrivetype mdt;
struct mtdrivetype_request mdt_req;
const char *name = (char *)NULL;
mdt_req.size = sizeof (struct mtdrivetype);
mdt_req.mtdtp = &mdt;
if (ioctl(mtfd, MTIOCGETDRIVETYPE, &mdt_req) == 0) {
name = mdt.name;
if (strstr(mdt.name, "Vendor '") != NULL) {
(void) printf("Unconfigured Drive: ");
}
} else {
perror("mt drivetype");
return (2);
}
if ((bp->mt_flags & MTF_SCSI)) {
if (name == (char *)NULL) {
name = "SCSI";
}
(void) printf("%s tape drive:\n", name);
(void) printf(" sense key(0x%x)= %s residual= %ld ",
bp->mt_erreg, print_key(bp->mt_erreg), bp->mt_resid);
(void) printf("retries= %d\n", bp->mt_dsreg);
if (bp->mt_fileno == -1) {
(void) printf(" file no= -1 block no= %lu\n",
(unsigned long)bp->mt_blkno);
} else {
(void) printf(" file no= %lu block no= %lu\n",
(unsigned long)bp->mt_fileno,
(unsigned long)bp->mt_blkno);
}
if ((bp->mt_flags & MTF_WORM_MEDIA) != 0) {
(void) printf(" WORM media\n");
}
} else {
if (name == NULL) {
(void) printf("unknown tape drive type (0x%x)\n",
mdt.type);
return (2);
}
(void) printf("%s tape drive:\n residual= %ld", name,
bp->mt_resid);
printreg(" ds", (ushort_t)bp->mt_dsreg, 0);
printreg(" er", (ushort_t)bp->mt_erreg, 0);
(void) putchar('\n');
}
return (0);
}
static char *sense_keys[] = {
"No Additional Sense",
"Soft Error",
"Not Ready",
"Media Error",
"Hardware Error",
"Illegal Request",
"Unit Attention",
"Write Protected",
"Blank Check",
"Vendor Unique",
"Copy Aborted",
"Aborted Command",
"Equal Error",
"Volume Overflow",
"Miscompare Error",
"Reserved",
#ifdef sun
"fatal",
"timeout",
"EOF",
"EOT",
"length error",
"BOT",
"wrong tape media",
#endif
};
static char *
print_key(short key_code)
{
static char unknown[32];
if ((key_code >= 0) &&
(key_code < (sizeof (sense_keys) / sizeof (sense_keys[0])))) {
return (sense_keys[key_code]);
}
(void) sprintf(unknown, "unknown sense key: 0x%x",
(unsigned int) key_code);
return (unknown);
}
static void
printreg(char *s, ushort_t v, char *bits)
{
int i, any = 0;
char c;
if (bits && *bits == 8) {
(void) printf("%s = %o", s, v);
} else {
(void) printf("%s = %x", s, v);
}
bits++;
if (v && bits) {
(void) putchar('<');
while ((i = *bits++) != 0) {
if (v & (1 << (i-1))) {
if (any) {
(void) putchar(',');
}
any = 1;
for (; (c = *bits) > 32; bits++) {
(void) putchar(c);
}
} else {
for (; *bits > 32; bits++)
;
}
}
(void) putchar('>');
}
}