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