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