xref: /minix/minix/commands/mt/mt.c (revision 0a6a1f1d)
1 /*	mt 1.3 - magnetic tape control			Author: Kees J. Bot
2  *								4 Apr 1993
3  */
4 #define nil	NULL
5 #ifndef _POSIX_SOURCE
6 #define _POSIX_SOURCE	1
7 #endif
8 #include <sys/types.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <errno.h>
12 #include <unistd.h>
13 #include <fcntl.h>
14 #include <string.h>
15 #include <sys/ioctl.h>
16 #include <sys/mtio.h>
17 
18 /* Device status. */
19 #define DS_OK		0
20 #define DS_ERR		1
21 #define DS_EOF		2
22 
23 /* SCSI Sense key bits. */
24 #define SENSE_KEY	0x0F	/* The key part. */
25 #define SENSE_ILI	0x20	/* Illegal block size. */
26 #define SENSE_EOM	0x40	/* End-of-media. */
27 #define SENSE_EOF	0x80	/* Filemark reached. */
28 
29 /* Supported operations: */
30 
31 typedef struct tape_operation {
32 	int	op;		/* Opcode for MTIOCTOP ioctl (if any). */
33 	char	*cmd;		/* Command name. */
34 	int	lim;		/* Limits on count. */
35 } tape_operation_t;
36 
37 #define SELF	-1	/* Not a simple command, have to interpret. */
38 #define IGN	-1	/* Ignore count field (or accept anything.) */
39 #define NNG	 0	/* Nonnegative count field. */
40 #define POS	 1	/* Positive count field. */
41 
42 tape_operation_t tapeops[] = {
43 	{ MTWEOF,  "eof",      POS },	/* Write EOF mark */
44 	{ MTWEOF,  "weof",     POS },	/* Same */
45 	{ MTFSF,   "fsf",      POS },	/* Forward Space File */
46 	{ MTFSR,   "fsr",      POS },	/* Forward Space Record */
47 	{ MTBSF,   "bsf",      NNG },	/* Backward Space File */
48 	{ MTBSR,   "bsr",      POS },	/* Backward Space Record */
49 	{ MTEOM,   "eom",      IGN },	/* To End-Of-Media */
50 	{ MTREW,   "rewind",   IGN },	/* Rewind */
51 	{ MTOFFL,  "offline",  IGN },	/* Rewind and take offline */
52 	{ MTOFFL,  "rewoffl",  IGN },	/* Same */
53 	{ SELF,	   "status",   IGN },	/* Tape Status */
54 	{ MTRETEN, "retension",IGN },	/* Retension the tape */
55 	{ MTERASE, "erase",    IGN },	/* Erase the tape */
56 	{ MTSETDNSTY,  "density",  NNG },	/* Select density */
57 	{ MTSETBSIZ,  "blksize",  NNG },	/* Select block size */
58 	{ MTSETBSIZ,  "blocksize",NNG },	/* Same */
59 };
60 
61 #define arraysize(a)	(sizeof(a)/sizeof((a)[0]))
62 #define arraylimit(a)	((a) + arraysize(a))
63 
64 /* From aha_scsi.c: */
65 char *dev_state[] = {
66 	"OK", "ERR", "EOF"
67 };
68 
69 char *scsi_sense[] = {
70 	"NO SENSE INFO", "RECOVERED ERROR", "NOT READY", "MEDIUM ERROR",
71 	"HARDWARE ERROR", "ILLEGAL REQUEST", "UNIT ATTENTION", "DATA PROTECT",
72 	"BLANK CHECK", "VENDOR UNIQUE ERROR", "COPY ABORTED", "ABORTED COMMAND",
73 	"EQUAL", "VOLUME OVERFLOW", "MISCOMPARE", "SENSE RESERVED"
74 };
75 
76 void usage(void)
77 {
78 	fprintf(stderr, "Usage: mt [-f device] command [count]\n");
79 	exit(1);
80 }
81 
82 int main(int argc, char **argv)
83 {
84 	char *tape;
85 	char *cmd;
86 	int count= 1;
87 	int fd, r;
88 	tape_operation_t *op, *found;
89 	struct mtop mtop;
90 	struct mtget mtget;
91 
92 	tape= getenv("TAPE");
93 
94 	/* -f tape? */
95 	if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'f') {
96 		tape= argv[1] + 2;
97 
98 		if (*tape == 0) {
99 			if (--argc < 2) usage();
100 			argv++;
101 			tape= argv[1];
102 		}
103 		argc--;
104 		argv++;
105 	}
106 
107 	if (argc != 2 && argc != 3) usage();
108 
109 	if (argc == 3) {
110 		/* Check and convert the 'count' argument. */
111 		char *end;
112 
113 		errno= 0;
114 		count= strtol(argv[2], &end, 0);
115 		if (*end != 0) usage();
116 		if (errno == ERANGE || (mtop.mt_count= count) != count) {
117 			fprintf(stderr, "mt: %s: count too large, overflow\n",
118 				argv[2]);
119 			exit(1);
120 		}
121 	}
122 
123 	if (tape == nil) {
124 		fprintf(stderr,
125 			"mt: tape device not specified by -f or $TAPE\n");
126 		exit(1);
127 	}
128 
129 	cmd= argv[1];
130 	if (strcmp(cmd, "rew") == 0) cmd= "rewind";	/* aha! */
131 	found= nil;
132 
133 	/* Search for an operation that is unambiguously named. */
134 	for (op= tapeops; op < arraylimit(tapeops); op++) {
135 		if (strncmp(op->cmd, cmd, strlen(cmd)) == 0) {
136 			if (found != nil) {
137 				fprintf(stderr, "mt: %s: ambiguous\n", cmd);
138 				exit(1);
139 			}
140 			found= op;
141 		}
142 	}
143 
144 	if ((op= found) == nil) {
145 		fprintf(stderr, "mt: unknown command '%s'\n", cmd);
146 		exit(1);
147 	}
148 
149 	/* Check count. */
150 	switch (op->lim) {
151 	case NNG:
152 		if (count < 0) {
153 			fprintf(stderr, "mt %s: count may not be negative\n",
154 				op->cmd);
155 			exit(1);
156 		}
157 		break;
158 	case POS:
159 		if (count <= 0) {
160 			fprintf(stderr,
161 				"mt %s: count must be greater than zero\n",
162 				op->cmd);
163 			exit(1);
164 		}
165 		break;
166 	}
167 
168 	if (strcmp(tape, "-") == 0) {
169 		fd= 0;
170 	} else
171 	if ((fd= open(tape, O_RDONLY)) < 0) {
172 		fprintf(stderr, "mt: %s: %s\n", tape, strerror(errno));
173 		exit(1);
174 	}
175 
176 	if (op->op != SELF) {
177 		/* A simple tape operation. */
178 
179 		mtop.mt_op= op->op;
180 		mtop.mt_count= count;
181 		r= ioctl(fd, MTIOCTOP, &mtop);
182 	} else
183 	if (strcmp(op->cmd, "status") == 0) {
184 		/* Get status information. */
185 
186 		if ((r= ioctl(fd, MTIOCGET, &mtget)) == 0) {
187 			printf("\
188 SCSI tape drive %s:\n\
189    drive status = 0x%02x (%s), sense key = 0x%02x (%s%s%s%s)\n\
190    file no = %ld, block no = %ld, residual = %ld, block size = ",
191    				tape, mtget.mt_dsreg,
192    				mtget.mt_dsreg > 2 ? "?" :
193    						dev_state[mtget.mt_dsreg],
194 				mtget.mt_erreg,
195 				mtget.mt_erreg & SENSE_EOF ? "EOF + " : "",
196 				mtget.mt_erreg & SENSE_EOM ? "EOM + " : "",
197 				mtget.mt_erreg & SENSE_ILI ? "ILI + " : "",
198 				scsi_sense[mtget.mt_erreg & SENSE_KEY],
199 				(long) mtget.mt_fileno,
200 				(long) mtget.mt_blkno,
201 				(long) mtget.mt_resid);
202 			if (mtget.mt_blksiz == 0) printf("variable\n");
203 			else printf("%d\n", mtget.mt_blksiz);
204 		}
205 	}
206 	if (r < 0) {
207 		if (errno == ENOTTY) {
208 			fprintf(stderr, "mt: %s: command '%s' not supported\n",
209 				tape, op->cmd);
210 			exit(2);
211 		}
212 		fprintf(stderr, "mt: %s: %s\n", tape, strerror(errno));
213 		exit(1);
214 	}
215 	exit(0);
216 }
217