1 /*
2 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5
6 /*
7 * Copyright (c) 1980 Regents of the University of California.
8 * All rights reserved. The Berkeley Software License Agreement
9 * specifies the terms and conditions for redistribution.
10 */
11
12 /*
13 * mt -- magnetic tape manipulation program
14 */
15 #include <stdio.h>
16 #include <ctype.h>
17
18 #include <errno.h>
19 #include <sys/types.h>
20 #include <sys/mtio.h>
21 #include <sys/ioctl.h>
22 #include <sys/param.h>
23 #include <sys/buf.h>
24 #include <sys/conf.h>
25 #include <sys/file.h>
26 #include <sys/uio.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <sys/stat.h>
31 #include <sys/scsi/targets/stdef.h>
32 #include <fcntl.h>
33
34
35 #define equal(s1, s2) (strcmp(s1, s2) == 0)
36 #define MTASF 100 /* absolute file positioning; first file is 0 */
37
38 /*
39 * This can't be DEFTAPE in mtio.h because that is currently the rewinding
40 * unit which makes 'mt fsf' a questionable activity at best.
41 */
42 #define DEFAULT_NRW_TAPE "/dev/rmt/0n"
43
44 static int print_config(int mtfd);
45 static char *print_key(short key_code);
46 static void printreg(char *, ushort_t, char *);
47 static int status(int mtfd, struct mtget *);
48
49 /* Pseudo flag for open even if drive is not ready (Unloaded) or reserved */
50 #define O_UNLOAD (O_RDWR | O_NDELAY)
51
52 static const struct commands {
53 char *c_name;
54 int c_code;
55 int c_oflag;
56 int c_usecnt;
57 } com[] = {
58 { "weof", MTWEOF, O_RDWR, 1 },
59 { "eof", MTWEOF, O_RDWR, 1 },
60 { "fsf", MTFSF, O_RDONLY, 1 },
61 { "bsf", MTBSF, O_RDONLY, 1 },
62 { "asf", MTASF, O_RDONLY, 1 },
63 { "fsr", MTFSR, O_RDONLY, 1 },
64 { "bsr", MTBSR, O_RDONLY, 1 },
65 { "rewind", MTREW, O_RDONLY, 0 },
66 { "offline", MTOFFL, O_RDONLY, 0 },
67 { "rewoffl", MTOFFL, O_RDONLY, 0 },
68 { "status", MTNOP, O_RDONLY, 0 },
69 { "retension", MTRETEN, O_RDONLY, 0 },
70 { "erase", MTERASE, O_RDWR, 0 },
71 { "eom", MTEOM, O_RDONLY, 0 },
72 { "nbsf", MTNBSF, O_RDONLY, 1 },
73 { "reserve", MTIOCRESERVE, O_RDONLY, 0 },
74 { "release", MTIOCRELEASE, O_RDONLY, 0 },
75 { "forcereserve", MTIOCFORCERESERVE, O_UNLOAD, 0 },
76 { "config", MTIOCGETDRIVETYPE, O_UNLOAD, 0 },
77 { "fssf", MTFSSF, O_RDONLY, 1 },
78 { "bssf", MTBSSF, O_RDONLY, 1 },
79 { "tell", MTTELL, O_RDONLY, 0 },
80 { "seek", MTSEEK, O_RDONLY, 1 },
81 { "load", MTLOAD, O_UNLOAD, 0 },
82 { "lock", MTLOCK, O_RDONLY, 0 },
83 { "unlock", MTUNLOCK, O_RDONLY, 0 },
84 { 0 }
85 };
86
87
88 int
main(int argc,char ** argv)89 main(int argc, char **argv)
90 {
91 char *cp;
92 char *tape;
93 int mtfd;
94 struct commands const *comp;
95 struct mtget mt_status;
96 struct mtlop mt_com;
97
98 if (argc > 2 && (equal(argv[1], "-t") || equal(argv[1], "-f"))) {
99 argc -= 2;
100 tape = argv[2];
101 argv += 2;
102 } else {
103 tape = getenv("TAPE");
104 if (tape == NULL) {
105 tape = DEFAULT_NRW_TAPE;
106 }
107 }
108
109 if (argc < 2) {
110 (void) fprintf(stderr,
111 "usage: mt [ -f device ] command [ count ]\n");
112 return (1);
113 }
114
115 cp = argv[1];
116 for (comp = com; comp->c_name != NULL; comp++) {
117 if (strncmp(cp, comp->c_name, strlen(cp)) == 0) {
118 break;
119 }
120 }
121
122 if (comp->c_name == NULL) {
123 (void) fprintf(stderr, "mt: unknown command: %s\n", cp);
124 return (1);
125 }
126
127 mtfd = open(tape, comp->c_oflag);
128 if (mtfd < 0) {
129
130 /*
131 * Provide additional error message decoding since
132 * we need additional error codes to fix them problem.
133 */
134 if (errno == EIO) {
135 (void) fprintf(stderr,
136 "%s: no tape loaded or drive offline\n", tape);
137 } else if (errno == EACCES) {
138 (void) fprintf(stderr,
139 "%s: write protected or reserved.\n", tape);
140 } else {
141 perror(tape);
142 }
143 return (1);
144 }
145
146 if (comp->c_code == MTIOCFORCERESERVE ||
147 comp->c_code == MTIOCRESERVE ||
148 comp->c_code == MTIOCRELEASE) {
149 /*
150 * Handle all MTIOC ioctls used in
151 * reservation/release/takeownership.
152 */
153 if (ioctl(mtfd, comp->c_code) < 0) {
154 perror("mt");
155 return (2);
156 }
157 } else if (comp->c_code == MTASF) {
158 /*
159 * Handle absolute file positioning. Ask tape driver
160 * where tape is and then skip to desired file. If
161 * driver doesn't support get location ioctl, rewind
162 * the tape and then space to the desired file.
163 */
164 int usecnt;
165 daddr_t mt_fileno;
166
167 usecnt = argc > 2 && comp->c_usecnt;
168 mt_fileno = usecnt ? atol(argv[2]) : 1;
169 if (mt_fileno < 0) {
170 (void) fprintf(stderr, "mt: negative file number\n");
171 return (1);
172 }
173 (void) ioctl(mtfd, MTIOCGET, (char *)&mt_status);
174 if (ioctl(mtfd, MTIOCGET, (char *)&mt_status) < 0) {
175 perror("mt");
176 return (2);
177 }
178 /*
179 * Check if device supports reporting current file
180 * tape file position. If not, rewind the tape, and
181 * space forward.
182 *
183 * If file number is -1 tape position is unknown!
184 */
185 if ((mt_status.mt_flags & MTF_ASF) == 0 ||
186 (mt_status.mt_fileno == -1)) {
187 /* printf("mt: rewind\n"); */
188 mt_com.mt_count = 1;
189 mt_com.mt_op = MTREW;
190 if (ioctl(mtfd, MTIOCLTOP, &mt_com) < 0) {
191 (void) fprintf(stderr, "%s %s %ld ",
192 tape, comp->c_name, mt_fileno);
193 perror("mt");
194 return (2);
195 }
196 /* Needed to rewind which worked now correct fileno */
197 mt_status.mt_fileno = 0;
198 mt_status.mt_blkno = 0;
199 }
200 if (mt_fileno < mt_status.mt_fileno) {
201 mt_com.mt_op = MTNBSF;
202 mt_com.mt_count = mt_status.mt_fileno - mt_fileno;
203 /* printf("mt: bsf= %d\n", mt_com.mt_count); */
204 } else {
205 mt_com.mt_op = MTFSF;
206 mt_com.mt_count = mt_fileno - mt_status.mt_fileno;
207 /* printf("mt: fsf= %d\n", mt_com.mt_count); */
208 }
209 if (ioctl(mtfd, MTIOCLTOP, &mt_com) < 0) {
210 (void) fprintf(stderr, "%s %s %ld ", tape, comp->c_name,
211 mt_fileno);
212 perror("failed");
213 return (2);
214 }
215 } else if (comp->c_code == MTIOCGETDRIVETYPE) {
216 return (print_config(mtfd));
217
218 /* Handle regular mag tape ioctls */
219 } else if (comp->c_code != MTNOP) {
220 int usecnt;
221
222 mt_com.mt_op = comp->c_code;
223 usecnt = argc > 2 && comp->c_usecnt;
224 mt_com.mt_count = (usecnt ? atoll(argv[2]) : 1);
225 if (mt_com.mt_count < 0) {
226 (void) fprintf(stderr, "mt: negative %s count\n",
227 comp->c_name);
228 return (1);
229 }
230 if (ioctl(mtfd, MTIOCLTOP, &mt_com) < 0) {
231 /*
232 * If we asked for a seek and it returns a tell
233 * we attempted to seek more then there was.
234 */
235 if (mt_com.mt_op == MTTELL &&
236 comp->c_code == MTSEEK) {
237 (void) printf("partial seek:at block = %llu.\n",
238 mt_com.mt_count);
239 } else {
240 (void) fprintf(stderr, "%s %s %lld ", tape,
241 comp->c_name, mt_com.mt_count);
242 perror("failed");
243 }
244 return (2);
245 }
246 if (mt_com.mt_op == MTTELL) {
247 (void) printf("At block = %llu.\n", mt_com.mt_count);
248 }
249
250
251 /* Handle status ioctl */
252 } else {
253 if (ioctl(mtfd, MTIOCGET, (char *)&mt_status) < 0) {
254 perror("mt");
255 return (2);
256 }
257 return (status(mtfd, &mt_status));
258 }
259 return (0);
260 }
261
262 static int
print_config(int mtfd)263 print_config(int mtfd)
264 {
265 struct mtdrivetype mdt;
266 struct mtdrivetype_request mdt_req;
267 char cfgname[48];
268 char tmp[2];
269 char *name;
270 int i;
271
272 mdt_req.size = sizeof (mdt);
273 mdt_req.mtdtp = &mdt;
274
275 if (ioctl(mtfd, MTIOCGETDRIVETYPE, &mdt_req) != 0) {
276 perror("mt config");
277 return (2);
278 }
279
280 /*
281 * remove trailing spaces from product id.
282 */
283 for (i = VIDPIDLEN; i; i--) {
284 if (isspace(mdt.vid[i]) || mdt.vid[i] == '*') {
285 mdt.vid[i] = 0;
286 } else if (mdt.vid[i] == 0) {
287 continue;
288 } else {
289 break;
290 }
291 }
292
293 /*
294 * If this is a generic name display the Vid and Pid instead.
295 */
296 if (strstr(mdt.name, "Vendor '") == NULL) {
297 name = mdt.name;
298 } else {
299 name = mdt.vid;
300 }
301
302 /*
303 * Attempt to create a configuration name using vid and pid.
304 */
305 (void) strcpy(cfgname, "CFG");
306
307 for (tmp[1] = i = 0; i < VIDPIDLEN; i++) {
308 if (!isalnum(name[i]))
309 continue;
310 if (isspace(name[i]))
311 continue;
312 tmp[0] = toupper(name[i]);
313 (void) strncat(cfgname, tmp, 1);
314 }
315
316 (void) printf("\"%s\", \"%s\", \"%s\";\n", mdt.vid, name, cfgname);
317
318 /*
319 * Don't want show some bits, ST_DYNAMIC is set in the driver
320 * so one can tell that its not a compiled in config.
321 * The ST_LONG_ERASE and ST_LONG_TIMEOUTS are not displayed
322 * becouse the timeout values below already reflect them being
323 * set.
324 * Also ST_KNOWS_MEDIA is not displayed as it can not be configured
325 * from an st.conf entry.
326 */
327 (void) printf("%s = 2,0x%X,%d,0x%X,", cfgname,
328 mdt.type, mdt.bsize, mdt.options &
329 ~(ST_DYNAMIC | ST_LONG_ERASE | ST_LONG_TIMEOUTS | ST_KNOWS_MEDIA));
330
331 (void) printf("4,0x%2.2X,0x%2.2X,0x%2.2X,0x%2.2X,%d,",
332 mdt.densities[0], mdt.densities[1], mdt.densities[2],
333 mdt.densities[3], mdt.default_density >> 3);
334
335 (void) printf("%d,%d,%d,%d,%d,%d,%d;\n", mdt.non_motion_timeout,
336 mdt.io_timeout, mdt.rewind_timeout, mdt.space_timeout,
337 mdt.load_timeout, mdt.unload_timeout, mdt.erase_timeout);
338
339 return (0);
340 }
341
342 /*
343 * Interpret the status buffer returned
344 */
345 static int
status(int mtfd,struct mtget * bp)346 status(int mtfd, struct mtget *bp)
347 {
348 struct mtdrivetype mdt;
349 struct mtdrivetype_request mdt_req;
350 const char *name = (char *)NULL;
351
352 /*
353 * Make a call to MTIOCGETDRIVETYPE ioctl, Also use old method
354 * of MT_TAPE_INFO for now, but MT_TAPE_INFO should dissapear in 2.7
355 */
356 mdt_req.size = sizeof (struct mtdrivetype);
357 mdt_req.mtdtp = &mdt;
358
359 if (ioctl(mtfd, MTIOCGETDRIVETYPE, &mdt_req) == 0) {
360 name = mdt.name;
361 if (strstr(mdt.name, "Vendor '") != NULL) {
362 (void) printf("Unconfigured Drive: ");
363 }
364 } else {
365 perror("mt drivetype");
366 return (2);
367 }
368
369 /* Handle SCSI tape drives specially. */
370 if ((bp->mt_flags & MTF_SCSI)) {
371 if (name == (char *)NULL) {
372 name = "SCSI";
373 }
374
375
376 (void) printf("%s tape drive:\n", name);
377
378 (void) printf(" sense key(0x%x)= %s residual= %ld ",
379 bp->mt_erreg, print_key(bp->mt_erreg), bp->mt_resid);
380 (void) printf("retries= %d\n", bp->mt_dsreg);
381 /*
382 * Can overflow the signed numbers.
383 * fileno will be -1 on error but all other positions are
384 * positive. blkno will never be negative.
385 */
386 if (bp->mt_fileno == -1) {
387 (void) printf(" file no= -1 block no= %lu\n",
388 (unsigned long)bp->mt_blkno);
389 } else {
390 (void) printf(" file no= %lu block no= %lu\n",
391 (unsigned long)bp->mt_fileno,
392 (unsigned long)bp->mt_blkno);
393 }
394 if ((bp->mt_flags & MTF_WORM_MEDIA) != 0) {
395 (void) printf(" WORM media\n");
396 }
397 } else {
398 /* Handle non-SCSI drives here. */
399 if (name == NULL) {
400 (void) printf("unknown tape drive type (0x%x)\n",
401 mdt.type);
402 return (2);
403 }
404 (void) printf("%s tape drive:\n residual= %ld", name,
405 bp->mt_resid);
406 printreg(" ds", (ushort_t)bp->mt_dsreg, 0);
407 printreg(" er", (ushort_t)bp->mt_erreg, 0);
408 (void) putchar('\n');
409 }
410 return (0);
411 }
412
413
414 /*
415 * Define SCSI sense key error messages.
416 *
417 * The first 16 sense keys are SCSI standard
418 * sense keys. The keys after this are
419 * Sun Specifice 'sense' keys- e.g., crap.
420 */
421
422 static char *sense_keys[] = {
423 "No Additional Sense", /* 0x00 */
424 "Soft Error", /* 0x01 */
425 "Not Ready", /* 0x02 */
426 "Media Error", /* 0x03 */
427 "Hardware Error", /* 0x04 */
428 "Illegal Request", /* 0x05 */
429 "Unit Attention", /* 0x06 */
430 "Write Protected", /* 0x07 */
431 "Blank Check", /* 0x08 */
432 "Vendor Unique", /* 0x09 */
433 "Copy Aborted", /* 0x0a */
434 "Aborted Command", /* 0x0b */
435 "Equal Error", /* 0x0c */
436 "Volume Overflow", /* 0x0d */
437 "Miscompare Error", /* 0x0e */
438 "Reserved", /* 0x0f */
439 #ifdef sun
440 "fatal", /* 0x10 */
441 "timeout", /* 0x11 */
442 "EOF", /* 0x12 */
443 "EOT", /* 0x13 */
444 "length error", /* 0x14 */
445 "BOT", /* 0x15 */
446 "wrong tape media", /* 0x16 */
447 #endif
448 };
449
450 /*
451 * Return the text string associated with the sense key value.
452 */
453 static char *
print_key(short key_code)454 print_key(short key_code)
455 {
456 static char unknown[32];
457
458 if ((key_code >= 0) &&
459 (key_code < (sizeof (sense_keys) / sizeof (sense_keys[0])))) {
460 return (sense_keys[key_code]);
461 }
462
463 (void) sprintf(unknown, "unknown sense key: 0x%x",
464 (unsigned int) key_code);
465 return (unknown);
466 }
467
468
469 /*
470 * Print a register a la the %b format of the kernel's printf
471 */
472 static void
printreg(char * s,ushort_t v,char * bits)473 printreg(char *s, ushort_t v, char *bits)
474 {
475 int i, any = 0;
476 char c;
477
478 if (bits && *bits == 8) {
479 (void) printf("%s = %o", s, v);
480 } else {
481 (void) printf("%s = %x", s, v);
482 }
483 bits++;
484 if (v && bits) {
485 (void) putchar('<');
486 while ((i = *bits++) != 0) {
487 if (v & (1 << (i-1))) {
488 if (any) {
489 (void) putchar(',');
490 }
491 any = 1;
492 for (; (c = *bits) > 32; bits++) {
493 (void) putchar(c);
494 }
495 } else {
496 for (; *bits > 32; bits++)
497 ;
498 }
499 }
500 (void) putchar('>');
501 }
502 }
503