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