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