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 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 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 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 * 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 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