1 /* 2 * Copyright (c) 1980, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 4. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 /*- 30 * Copyright (c) 2013, 2014, 2015 Spectra Logic Corporation 31 * All rights reserved. 32 * 33 * Redistribution and use in source and binary forms, with or without 34 * modification, are permitted provided that the following conditions 35 * are met: 36 * 1. Redistributions of source code must retain the above copyright 37 * notice, this list of conditions, and the following disclaimer, 38 * without modification. 39 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 40 * substantially similar to the "NO WARRANTY" disclaimer below 41 * ("Disclaimer") and any redistribution must be conditioned upon 42 * including a substantially similar Disclaimer requirement for further 43 * binary redistribution. 44 * 45 * NO WARRANTY 46 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 47 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 48 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR 49 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 50 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 51 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 52 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 53 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 54 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 55 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 56 * POSSIBILITY OF SUCH DAMAGES. 57 * 58 * Authors: Ken Merry (Spectra Logic Corporation) 59 */ 60 61 #ifndef lint 62 static const char copyright[] = 63 "@(#) Copyright (c) 1980, 1993\n\ 64 The Regents of the University of California. All rights reserved.\n"; 65 #endif /* not lint */ 66 67 #ifndef lint 68 #if 0 69 static char sccsid[] = "@(#)mt.c 8.2 (Berkeley) 5/4/95"; 70 #endif 71 #endif /* not lint */ 72 73 #include <sys/cdefs.h> 74 __FBSDID("$FreeBSD$"); 75 76 /* 77 * mt -- 78 * magnetic tape manipulation program 79 */ 80 #include <sys/types.h> 81 #include <sys/ioctl.h> 82 #include <sys/mtio.h> 83 #include <sys/queue.h> 84 #include <sys/sbuf.h> 85 86 #include <ctype.h> 87 #include <err.h> 88 #include <fcntl.h> 89 #include <stdio.h> 90 #include <stdlib.h> 91 #include <string.h> 92 #include <unistd.h> 93 #include <stdint.h> 94 #include <errno.h> 95 #include <bsdxml.h> 96 #include <mtlib.h> 97 98 #include <cam/cam.h> 99 #include <cam/cam_ccb.h> 100 #include <cam/cam_periph.h> 101 #include <cam/scsi/scsi_all.h> 102 #include <cam/scsi/scsi_sa.h> 103 104 /* the appropriate sections of <sys/mtio.h> are also #ifdef'd for FreeBSD */ 105 /* c_flags */ 106 #define NEED_2ARGS 0x01 107 #define ZERO_ALLOWED 0x02 108 #define IS_DENSITY 0x04 109 #define DISABLE_THIS 0x08 110 #define IS_COMP 0x10 111 #define USE_GETOPT 0x20 112 113 #ifndef TRUE 114 #define TRUE 1 115 #endif 116 #ifndef FALSE 117 #define FALSE 0 118 #endif 119 #ifndef MAX 120 #define MAX(a, b) (a > b) ? a : b 121 #endif 122 123 typedef enum { 124 MT_CMD_NONE = MTLOAD + 1, 125 MT_CMD_PROTECT, 126 MT_CMD_GETDENSITY 127 } mt_commands; 128 129 static const struct commands { 130 const char *c_name; 131 unsigned long c_code; 132 int c_ronly; 133 int c_flags; 134 } com[] = { 135 { "bsf", MTBSF, 1, 0 }, 136 { "bsr", MTBSR, 1, 0 }, 137 /* XXX FreeBSD considered "eof" dangerous, since it's being 138 confused with "eom" (and is an alias for "weof" anyway) */ 139 { "eof", MTWEOF, 0, DISABLE_THIS }, 140 { "fsf", MTFSF, 1, 0 }, 141 { "fsr", MTFSR, 1, 0 }, 142 { "offline", MTOFFL, 1, 0 }, 143 { "load", MTLOAD, 1, 0 }, 144 { "rewind", MTREW, 1, 0 }, 145 { "rewoffl", MTOFFL, 1, 0 }, 146 { "ostatus", MTNOP, 1, 0 }, 147 { "weof", MTWEOF, 0, ZERO_ALLOWED }, 148 { "weofi", MTWEOFI, 0, ZERO_ALLOWED }, 149 { "erase", MTERASE, 0, ZERO_ALLOWED}, 150 { "blocksize", MTSETBSIZ, 0, NEED_2ARGS|ZERO_ALLOWED }, 151 { "density", MTSETDNSTY, 0, NEED_2ARGS|ZERO_ALLOWED|IS_DENSITY }, 152 { "eom", MTEOD, 1, 0 }, 153 { "eod", MTEOD, 1, 0 }, 154 { "smk", MTWSS, 0, 0 }, 155 { "wss", MTWSS, 0, 0 }, 156 { "fss", MTFSS, 1, 0 }, 157 { "bss", MTBSS, 1, 0 }, 158 { "comp", MTCOMP, 0, NEED_2ARGS|ZERO_ALLOWED|IS_COMP }, 159 { "retension", MTRETENS, 1, 0 }, 160 { "rdhpos", MTIOCRDHPOS, 0, 0 }, 161 { "rdspos", MTIOCRDSPOS, 0, 0 }, 162 { "sethpos", MTIOCHLOCATE, 0, NEED_2ARGS|ZERO_ALLOWED }, 163 { "setspos", MTIOCSLOCATE, 0, NEED_2ARGS|ZERO_ALLOWED }, 164 { "errstat", MTIOCERRSTAT, 0, 0 }, 165 { "setmodel", MTIOCSETEOTMODEL, 0, NEED_2ARGS|ZERO_ALLOWED }, 166 { "seteotmodel", MTIOCSETEOTMODEL, 0, NEED_2ARGS|ZERO_ALLOWED }, 167 { "getmodel", MTIOCGETEOTMODEL, 0, 0 }, 168 { "geteotmodel", MTIOCGETEOTMODEL, 0, 0 }, 169 { "rblim", MTIOCRBLIM, 0, 0}, 170 { "getdensity", MT_CMD_GETDENSITY, 0, USE_GETOPT}, 171 { "status", MTIOCEXTGET, 0, USE_GETOPT }, 172 { "locate", MTIOCEXTLOCATE, 0, USE_GETOPT }, 173 { "param", MTIOCPARAMGET, 0, USE_GETOPT }, 174 { "protect", MT_CMD_PROTECT, 0, USE_GETOPT }, 175 { NULL, 0, 0, 0 } 176 }; 177 178 179 static const char *getblksiz(int); 180 static void printreg(const char *, u_int, const char *); 181 static void status(struct mtget *); 182 static void usage(void); 183 const char *get_driver_state_str(int dsreg); 184 static void st_status (struct mtget *); 185 static int mt_locate(int argc, char **argv, int mtfd, const char *tape); 186 static int nstatus_print(int argc, char **argv, char *xml_str, 187 struct mt_status_data *status_data); 188 static int mt_xml_cmd(unsigned long cmd, int argc, char **argv, int mtfd, 189 const char *tape); 190 static int mt_print_density_entry(struct mt_status_entry *density_root, int indent); 191 static int mt_print_density_report(struct mt_status_entry *report_root, int indent); 192 static int mt_print_density(struct mt_status_entry *density_root, int indent); 193 static int mt_getdensity(int argc, char **argv, char *xml_str, 194 struct mt_status_data *status_data); 195 static int mt_set_param(int mtfd, struct mt_status_data *status_data, 196 char *param_name, char *param_value); 197 static int mt_protect(int argc, char **argv, int mtfd, 198 struct mt_status_data *status_data); 199 static int mt_param(int argc, char **argv, int mtfd, char *xml_str, 200 struct mt_status_data *status_data); 201 static const char *denstostring (int d); 202 static u_int32_t stringtocomp(const char *s); 203 static const char *comptostring(u_int32_t comp); 204 static void warn_eof(void); 205 206 int 207 main(int argc, char *argv[]) 208 { 209 const struct commands *comp; 210 struct mtget mt_status; 211 struct mtop mt_com; 212 int ch, len, mtfd; 213 const char *p, *tape; 214 215 bzero(&mt_com, sizeof(mt_com)); 216 217 if ((tape = getenv("TAPE")) == NULL) 218 tape = DEFTAPE; 219 220 while ((ch = getopt(argc, argv, "f:t:")) != -1) 221 switch(ch) { 222 case 'f': 223 case 't': 224 tape = optarg; 225 break; 226 case '?': 227 usage(); 228 break; 229 default: 230 break; 231 } 232 argc -= optind; 233 argv += optind; 234 235 if (argc < 1) 236 usage(); 237 238 len = strlen(p = *argv++); 239 for (comp = com;; comp++) { 240 if (comp->c_name == NULL) 241 errx(1, "%s: unknown command", p); 242 if (strncmp(p, comp->c_name, len) == 0) 243 break; 244 } 245 if((comp->c_flags & NEED_2ARGS) && argc != 2) 246 usage(); 247 if(comp->c_flags & DISABLE_THIS) { 248 warn_eof(); 249 } 250 if (comp->c_flags & USE_GETOPT) { 251 argc--; 252 optind = 0; 253 } 254 255 if ((mtfd = open(tape, comp->c_ronly ? O_RDONLY : O_RDWR)) < 0) 256 err(1, "%s", tape); 257 if (comp->c_code != MTNOP) { 258 mt_com.mt_op = comp->c_code; 259 if (*argv) { 260 if (!isdigit(**argv) && 261 (comp->c_flags & IS_DENSITY)) { 262 const char *dcanon; 263 mt_com.mt_count = mt_density_num(*argv); 264 if (mt_com.mt_count == 0) 265 errx(1, "%s: unknown density", *argv); 266 dcanon = denstostring(mt_com.mt_count); 267 if (strcmp(dcanon, *argv) != 0) 268 printf( 269 "Using \"%s\" as an alias for %s\n", 270 *argv, dcanon); 271 p = ""; 272 } else if (!isdigit(**argv) && 273 (comp->c_flags & IS_COMP)) { 274 275 mt_com.mt_count = stringtocomp(*argv); 276 if ((u_int32_t)mt_com.mt_count == 0xf0f0f0f0) 277 errx(1, "%s: unknown compression", 278 *argv); 279 p = ""; 280 } else if ((comp->c_flags & USE_GETOPT) == 0) { 281 char *q; 282 /* allow for hex numbers; useful for density */ 283 mt_com.mt_count = strtol(*argv, &q, 0); 284 p = q; 285 } 286 if (((comp->c_flags & USE_GETOPT) == 0) 287 && (((mt_com.mt_count <= 288 ((comp->c_flags & ZERO_ALLOWED)? -1: 0)) 289 && ((comp->c_flags & IS_COMP) == 0)) 290 || *p)) 291 errx(1, "%s: illegal count", *argv); 292 } 293 else 294 mt_com.mt_count = 1; 295 switch (comp->c_code) { 296 case MTIOCERRSTAT: 297 { 298 unsigned int i; 299 union mterrstat umn; 300 struct scsi_tape_errors *s = &umn.scsi_errstat; 301 302 if (ioctl(mtfd, comp->c_code, (caddr_t)&umn) < 0) 303 err(2, "%s", tape); 304 (void)printf("Last I/O Residual: %u\n", s->io_resid); 305 (void)printf(" Last I/O Command:"); 306 for (i = 0; i < sizeof (s->io_cdb); i++) 307 (void)printf(" %02X", s->io_cdb[i]); 308 (void)printf("\n"); 309 (void)printf(" Last I/O Sense:\n\n\t"); 310 for (i = 0; i < sizeof (s->io_sense); i++) { 311 (void)printf(" %02X", s->io_sense[i]); 312 if (((i + 1) & 0xf) == 0) { 313 (void)printf("\n\t"); 314 } 315 } 316 (void)printf("\n"); 317 (void)printf("Last Control Residual: %u\n", 318 s->ctl_resid); 319 (void)printf(" Last Control Command:"); 320 for (i = 0; i < sizeof (s->ctl_cdb); i++) 321 (void)printf(" %02X", s->ctl_cdb[i]); 322 (void)printf("\n"); 323 (void)printf(" Last Control Sense:\n\n\t"); 324 for (i = 0; i < sizeof (s->ctl_sense); i++) { 325 (void)printf(" %02X", s->ctl_sense[i]); 326 if (((i + 1) & 0xf) == 0) { 327 (void)printf("\n\t"); 328 } 329 } 330 (void)printf("\n\n"); 331 exit(0); 332 /* NOTREACHED */ 333 } 334 case MTIOCRDHPOS: 335 case MTIOCRDSPOS: 336 { 337 u_int32_t block; 338 if (ioctl(mtfd, comp->c_code, (caddr_t)&block) < 0) 339 err(2, "%s", tape); 340 (void)printf("%s: %s block location %u\n", tape, 341 (comp->c_code == MTIOCRDHPOS)? "hardware" : 342 "logical", block); 343 exit(0); 344 /* NOTREACHED */ 345 } 346 case MTIOCSLOCATE: 347 case MTIOCHLOCATE: 348 { 349 u_int32_t block = (u_int32_t)mt_com.mt_count; 350 if (ioctl(mtfd, comp->c_code, (caddr_t)&block) < 0) 351 err(2, "%s", tape); 352 exit(0); 353 /* NOTREACHED */ 354 } 355 case MTIOCGETEOTMODEL: 356 { 357 u_int32_t om; 358 if (ioctl(mtfd, MTIOCGETEOTMODEL, (caddr_t)&om) < 0) 359 err(2, "%s", tape); 360 (void)printf("%s: the model is %u filemar%s at EOT\n", 361 tape, om, (om > 1)? "ks" : "k"); 362 exit(0); 363 /* NOTREACHED */ 364 } 365 case MTIOCSETEOTMODEL: 366 { 367 u_int32_t om, nm = (u_int32_t)mt_com.mt_count; 368 if (ioctl(mtfd, MTIOCGETEOTMODEL, (caddr_t)&om) < 0) 369 err(2, "%s", tape); 370 if (ioctl(mtfd, comp->c_code, (caddr_t)&nm) < 0) 371 err(2, "%s", tape); 372 (void)printf("%s: old model was %u filemar%s at EOT\n", 373 tape, om, (om > 1)? "ks" : "k"); 374 (void)printf("%s: new model is %u filemar%s at EOT\n", 375 tape, nm, (nm > 1)? "ks" : "k"); 376 exit(0); 377 /* NOTREACHED */ 378 } 379 case MTIOCRBLIM: 380 { 381 struct mtrblim rblim; 382 383 bzero(&rblim, sizeof(rblim)); 384 385 if (ioctl(mtfd, MTIOCRBLIM, (caddr_t)&rblim) < 0) 386 err(2, "%s", tape); 387 (void)printf("%s: min blocksize %u bytes, " 388 "max blocksize %u bytes, granularity %u bytes\n", 389 tape, rblim.min_block_length, 390 rblim.max_block_length, rblim.granularity); 391 exit(0); 392 /* NOTREACHED */ 393 } 394 case MTIOCPARAMGET: 395 case MTIOCEXTGET: 396 case MT_CMD_PROTECT: 397 case MT_CMD_GETDENSITY: 398 { 399 int retval = 0; 400 401 retval = mt_xml_cmd(comp->c_code, argc, argv, mtfd, 402 tape); 403 404 exit(retval); 405 } 406 case MTIOCEXTLOCATE: 407 { 408 int retval = 0; 409 410 retval = mt_locate(argc, argv, mtfd, tape); 411 412 exit(retval); 413 } 414 default: 415 break; 416 } 417 if (ioctl(mtfd, MTIOCTOP, &mt_com) < 0) 418 err(1, "%s: %s", tape, comp->c_name); 419 } else { 420 if (ioctl(mtfd, MTIOCGET, &mt_status) < 0) 421 err(1, NULL); 422 status(&mt_status); 423 } 424 exit(0); 425 /* NOTREACHED */ 426 } 427 428 static const struct tape_desc { 429 short t_type; /* type of magtape device */ 430 const char *t_name; /* printing name */ 431 const char *t_dsbits; /* "drive status" register */ 432 const char *t_erbits; /* "error" register */ 433 } tapes[] = { 434 { MT_ISAR, "SCSI tape drive", 0, 0 }, 435 { 0, NULL, 0, 0 } 436 }; 437 438 /* 439 * Interpret the status buffer returned 440 */ 441 static void 442 status(struct mtget *bp) 443 { 444 const struct tape_desc *mt; 445 446 for (mt = tapes;; mt++) { 447 if (mt->t_type == 0) { 448 (void)printf("%d: unknown tape drive type\n", 449 bp->mt_type); 450 return; 451 } 452 if (mt->t_type == bp->mt_type) 453 break; 454 } 455 if(mt->t_type == MT_ISAR) 456 st_status(bp); 457 else { 458 (void)printf("%s tape drive, residual=%d\n", 459 mt->t_name, bp->mt_resid); 460 printreg("ds", (unsigned short)bp->mt_dsreg, mt->t_dsbits); 461 printreg("\ner", (unsigned short)bp->mt_erreg, mt->t_erbits); 462 (void)putchar('\n'); 463 } 464 } 465 466 /* 467 * Print a register a la the %b format of the kernel's printf. 468 */ 469 static void 470 printreg(const char *s, u_int v, const char *bits) 471 { 472 int i, any = 0; 473 char c; 474 475 if (bits && *bits == 8) 476 printf("%s=%o", s, v); 477 else 478 printf("%s=%x", s, v); 479 if (!bits) 480 return; 481 bits++; 482 if (v && bits) { 483 putchar('<'); 484 while ((i = *bits++)) { 485 if (v & (1 << (i-1))) { 486 if (any) 487 putchar(','); 488 any = 1; 489 for (; (c = *bits) > 32; bits++) 490 putchar(c); 491 } else 492 for (; *bits > 32; bits++) 493 ; 494 } 495 putchar('>'); 496 } 497 } 498 499 static void 500 usage(void) 501 { 502 (void)fprintf(stderr, "usage: mt [-f device] command [count]\n"); 503 exit(1); 504 } 505 506 static const struct compression_types { 507 u_int32_t comp_number; 508 const char *name; 509 } comp_types[] = { 510 { 0x00, "none" }, 511 { 0x00, "off" }, 512 { 0x10, "IDRC" }, 513 { 0x20, "DCLZ" }, 514 { 0xffffffff, "enable" }, 515 { 0xffffffff, "on" }, 516 { 0xf0f0f0f0, NULL} 517 }; 518 519 static const char * 520 denstostring(int d) 521 { 522 static char buf[20]; 523 const char *name = mt_density_name(d); 524 525 if (name == NULL) 526 sprintf(buf, "0x%02x", d); 527 else 528 sprintf(buf, "0x%02x:%s", d, name); 529 return buf; 530 } 531 532 static const char * 533 getblksiz(int bs) 534 { 535 static char buf[25]; 536 if (bs == 0) 537 return "variable"; 538 else { 539 sprintf(buf, "%d bytes", bs); 540 return buf; 541 } 542 } 543 544 static const char * 545 comptostring(u_int32_t comp) 546 { 547 static char buf[20]; 548 const struct compression_types *ct; 549 550 if (comp == MT_COMP_DISABLED) 551 return "disabled"; 552 else if (comp == MT_COMP_UNSUPP) 553 return "unsupported"; 554 555 for (ct = comp_types; ct->name; ct++) 556 if (ct->comp_number == comp) 557 break; 558 559 if (ct->comp_number == 0xf0f0f0f0) { 560 sprintf(buf, "0x%x", comp); 561 return(buf); 562 } else 563 return(ct->name); 564 } 565 566 static u_int32_t 567 stringtocomp(const char *s) 568 { 569 const struct compression_types *ct; 570 size_t l = strlen(s); 571 572 for (ct = comp_types; ct->name; ct++) 573 if (strncasecmp(ct->name, s, l) == 0) 574 break; 575 576 return(ct->comp_number); 577 } 578 579 static struct driver_state { 580 int dsreg; 581 const char *desc; 582 } driver_states[] = { 583 { MTIO_DSREG_REST, "at rest" }, 584 { MTIO_DSREG_RBSY, "Communicating with drive" }, 585 { MTIO_DSREG_WR, "Writing" }, 586 { MTIO_DSREG_FMK, "Writing Filemarks" }, 587 { MTIO_DSREG_ZER, "Erasing" }, 588 { MTIO_DSREG_RD, "Reading" }, 589 { MTIO_DSREG_FWD, "Spacing Forward" }, 590 { MTIO_DSREG_REV, "Spacing Reverse" }, 591 { MTIO_DSREG_POS, "Hardware Positioning (direction unknown)" }, 592 { MTIO_DSREG_REW, "Rewinding" }, 593 { MTIO_DSREG_TEN, "Retensioning" }, 594 { MTIO_DSREG_UNL, "Unloading" }, 595 { MTIO_DSREG_LD, "Loading" }, 596 }; 597 598 const char * 599 get_driver_state_str(int dsreg) 600 { 601 unsigned int i; 602 603 for (i = 0; i < (sizeof(driver_states)/sizeof(driver_states[0])); i++) { 604 if (driver_states[i].dsreg == dsreg) 605 return (driver_states[i].desc); 606 } 607 608 return (NULL); 609 } 610 611 static void 612 st_status(struct mtget *bp) 613 { 614 printf("Mode Density Blocksize bpi " 615 "Compression\n" 616 "Current: %-17s %-12s %-7d %s\n" 617 "---------available modes---------\n" 618 "0: %-17s %-12s %-7d %s\n" 619 "1: %-17s %-12s %-7d %s\n" 620 "2: %-17s %-12s %-7d %s\n" 621 "3: %-17s %-12s %-7d %s\n", 622 denstostring(bp->mt_density), getblksiz(bp->mt_blksiz), 623 mt_density_bp(bp->mt_density, TRUE), comptostring(bp->mt_comp), 624 denstostring(bp->mt_density0), getblksiz(bp->mt_blksiz0), 625 mt_density_bp(bp->mt_density0, TRUE), comptostring(bp->mt_comp0), 626 denstostring(bp->mt_density1), getblksiz(bp->mt_blksiz1), 627 mt_density_bp(bp->mt_density1, TRUE), comptostring(bp->mt_comp1), 628 denstostring(bp->mt_density2), getblksiz(bp->mt_blksiz2), 629 mt_density_bp(bp->mt_density2, TRUE), comptostring(bp->mt_comp2), 630 denstostring(bp->mt_density3), getblksiz(bp->mt_blksiz3), 631 mt_density_bp(bp->mt_density3, TRUE), comptostring(bp->mt_comp3)); 632 633 if (bp->mt_dsreg != MTIO_DSREG_NIL) { 634 const char sfmt[] = "Current Driver State: %s.\n"; 635 printf("---------------------------------\n"); 636 const char *state_str; 637 638 state_str = get_driver_state_str(bp->mt_dsreg); 639 if (state_str == NULL) { 640 char foo[32]; 641 (void) sprintf(foo, "Unknown state 0x%x", bp->mt_dsreg); 642 printf(sfmt, foo); 643 } else { 644 printf(sfmt, state_str); 645 } 646 } 647 if (bp->mt_resid == 0 && bp->mt_fileno == (daddr_t) -1 && 648 bp->mt_blkno == (daddr_t) -1) 649 return; 650 printf("---------------------------------\n"); 651 printf("File Number: %d\tRecord Number: %d\tResidual Count %d\n", 652 bp->mt_fileno, bp->mt_blkno, bp->mt_resid); 653 } 654 655 static int 656 mt_locate(int argc, char **argv, int mtfd, const char *tape) 657 { 658 struct mtlocate mtl; 659 uint64_t logical_id = 0; 660 mt_locate_dest_type dest_type = MT_LOCATE_DEST_FILE; 661 int eod = 0, explicit = 0, immediate = 0; 662 int64_t partition = 0; 663 int block_addr_set = 0, partition_set = 0, file_set = 0, set_set = 0; 664 int c, retval; 665 666 retval = 0; 667 bzero(&mtl, sizeof(mtl)); 668 669 while ((c = getopt(argc, argv, "b:eEf:ip:s:")) != -1) { 670 switch (c) { 671 case 'b': 672 /* Block address */ 673 logical_id = strtoull(optarg, NULL, 0); 674 dest_type = MT_LOCATE_DEST_OBJECT; 675 block_addr_set = 1; 676 break; 677 case 'e': 678 /* end of data */ 679 eod = 1; 680 dest_type = MT_LOCATE_DEST_EOD; 681 break; 682 case 'E': 683 /* 684 * XXX KDM explicit address mode. Should we even 685 * allow this, since the driver doesn't operate in 686 * explicit address mode? 687 */ 688 explicit = 1; 689 break; 690 case 'f': 691 /* file number */ 692 logical_id = strtoull(optarg, NULL, 0); 693 dest_type = MT_LOCATE_DEST_FILE; 694 file_set = 1; 695 break; 696 case 'i': 697 /* 698 * Immediate address mode. XXX KDM do we want to 699 * implement this? The other commands in the 700 * tape driver will need to be able to handle this. 701 */ 702 immediate = 1; 703 break; 704 case 'p': 705 /* 706 * Change partition to the given partition. 707 */ 708 partition = strtol(optarg, NULL, 0); 709 partition_set = 1; 710 break; 711 case 's': 712 /* Go to the given set mark */ 713 logical_id = strtoull(optarg, NULL, 0); 714 dest_type = MT_LOCATE_DEST_SET; 715 set_set = 1; 716 break; 717 default: 718 break; 719 } 720 } 721 722 /* 723 * These options are mutually exclusive. The user may only specify 724 * one. 725 */ 726 if ((block_addr_set + file_set + eod + set_set) != 1) 727 errx(1, "You must specify only one of -b, -f, -e, or -s"); 728 729 mtl.dest_type = dest_type; 730 switch (dest_type) { 731 case MT_LOCATE_DEST_OBJECT: 732 case MT_LOCATE_DEST_FILE: 733 case MT_LOCATE_DEST_SET: 734 mtl.logical_id = logical_id; 735 break; 736 case MT_LOCATE_DEST_EOD: 737 break; 738 } 739 740 if (immediate != 0) 741 mtl.flags |= MT_LOCATE_FLAG_IMMED; 742 743 if (partition_set != 0) { 744 mtl.flags |= MT_LOCATE_FLAG_CHANGE_PART; 745 mtl.partition = partition; 746 } 747 748 if (explicit != 0) 749 mtl.block_address_mode = MT_LOCATE_BAM_EXPLICIT; 750 else 751 mtl.block_address_mode = MT_LOCATE_BAM_IMPLICIT; 752 753 if (ioctl(mtfd, MTIOCEXTLOCATE, &mtl) == -1) 754 err(1, "MTIOCEXTLOCATE ioctl failed on %s", tape); 755 756 return (retval); 757 } 758 759 typedef enum { 760 MT_PERIPH_NAME = 0, 761 MT_UNIT_NUMBER = 1, 762 MT_VENDOR = 2, 763 MT_PRODUCT = 3, 764 MT_REVISION = 4, 765 MT_COMPRESSION_SUPPORTED = 5, 766 MT_COMPRESSION_ENABLED = 6, 767 MT_COMPRESSION_ALGORITHM = 7, 768 MT_MEDIA_DENSITY = 8, 769 MT_MEDIA_BLOCKSIZE = 9, 770 MT_CALCULATED_FILENO = 10, 771 MT_CALCULATED_REL_BLKNO = 11, 772 MT_REPORTED_FILENO = 12, 773 MT_REPORTED_BLKNO = 13, 774 MT_PARTITION = 14, 775 MT_BOP = 15, 776 MT_EOP = 16, 777 MT_BPEW = 17, 778 MT_DSREG = 18, 779 MT_RESID = 19, 780 MT_FIXED_MODE = 20, 781 MT_SERIAL_NUM = 21, 782 MT_MAXIO = 22, 783 MT_CPI_MAXIO = 23, 784 MT_MAX_BLK = 24, 785 MT_MIN_BLK = 25, 786 MT_BLK_GRAN = 26, 787 MT_MAX_EFF_IOSIZE = 27 788 } status_item_index; 789 790 static struct mt_status_items { 791 const char *name; 792 struct mt_status_entry *entry; 793 } req_status_items[] = { 794 { "periph_name", NULL }, 795 { "unit_number", NULL }, 796 { "vendor", NULL }, 797 { "product", NULL }, 798 { "revision", NULL }, 799 { "compression_supported", NULL }, 800 { "compression_enabled", NULL }, 801 { "compression_algorithm", NULL }, 802 { "media_density", NULL }, 803 { "media_blocksize", NULL }, 804 { "calculated_fileno", NULL }, 805 { "calculated_rel_blkno", NULL }, 806 { "reported_fileno", NULL }, 807 { "reported_blkno", NULL }, 808 { "partition", NULL }, 809 { "bop", NULL }, 810 { "eop", NULL }, 811 { "bpew", NULL }, 812 { "dsreg", NULL }, 813 { "residual", NULL }, 814 { "fixed_mode", NULL }, 815 { "serial_num", NULL }, 816 { "maxio", NULL }, 817 { "cpi_maxio", NULL }, 818 { "max_blk", NULL }, 819 { "min_blk", NULL }, 820 { "blk_gran", NULL }, 821 { "max_effective_iosize", NULL } 822 }; 823 824 int 825 nstatus_print(int argc, char **argv, char *xml_str, 826 struct mt_status_data *status_data) 827 { 828 unsigned int i; 829 int64_t calculated_fileno, calculated_rel_blkno; 830 int64_t rep_fileno, rep_blkno, partition, resid; 831 char block_str[32]; 832 const char *dens_str; 833 int dsreg, bop, eop, bpew; 834 int xml_dump = 0; 835 size_t dens_len; 836 unsigned int field_width; 837 int verbose = 0; 838 int c; 839 840 while ((c = getopt(argc, argv, "xv")) != -1) { 841 switch (c) { 842 case 'x': 843 xml_dump = 1; 844 break; 845 case 'v': 846 verbose = 1; 847 break; 848 default: 849 break; 850 } 851 } 852 853 if (xml_dump != 0) { 854 printf("%s", xml_str); 855 return (0); 856 } 857 858 for (i = 0; i < (sizeof(req_status_items)/sizeof(req_status_items[0])); 859 i++) { 860 char *name; 861 862 name = __DECONST(char *, req_status_items[i].name); 863 req_status_items[i].entry = mt_status_entry_find(status_data, 864 name); 865 if (req_status_items[i].entry == NULL) { 866 errx(1, "Cannot find status entry %s", 867 req_status_items[i].name); 868 } 869 } 870 871 printf("Drive: %s%ju: <%s %s %s> Serial Number: %s\n", 872 req_status_items[MT_PERIPH_NAME].entry->value, 873 (uintmax_t)req_status_items[MT_UNIT_NUMBER].entry->value_unsigned, 874 req_status_items[MT_VENDOR].entry->value, 875 req_status_items[MT_PRODUCT].entry->value, 876 req_status_items[MT_REVISION].entry->value, 877 (req_status_items[MT_SERIAL_NUM].entry->value) ? 878 req_status_items[MT_SERIAL_NUM].entry->value : "none"); 879 printf("---------------------------------\n"); 880 881 /* 882 * We check to see whether we're in fixed mode or not, and don't 883 * just believe the blocksize. If the SILI bit is turned on, the 884 * blocksize will be set to 4, even though we're doing variable 885 * length (well, multiples of 4) blocks. 886 */ 887 if (req_status_items[MT_FIXED_MODE].entry->value_signed == 0) 888 snprintf(block_str, sizeof(block_str), "variable"); 889 else 890 snprintf(block_str, sizeof(block_str), "%s", 891 getblksiz(req_status_items[ 892 MT_MEDIA_BLOCKSIZE].entry->value_unsigned)); 893 894 dens_str = denstostring(req_status_items[ 895 MT_MEDIA_DENSITY].entry->value_unsigned); 896 if (dens_str == NULL) 897 dens_len = 0; 898 else 899 dens_len = strlen(dens_str); 900 field_width = MAX(dens_len, 17); 901 printf("Mode %-*s Blocksize bpi Compression\n" 902 "Current: %-*s %-12s %-7d ", 903 field_width, "Density", field_width, dens_str, block_str, 904 mt_density_bp(req_status_items[ 905 MT_MEDIA_DENSITY].entry->value_unsigned, TRUE)); 906 907 if (req_status_items[MT_COMPRESSION_SUPPORTED].entry->value_signed == 0) 908 printf("unsupported\n"); 909 else if (req_status_items[ 910 MT_COMPRESSION_ENABLED].entry->value_signed == 0) 911 printf("disabled\n"); 912 else { 913 printf("enabled (%s)\n", 914 comptostring(req_status_items[ 915 MT_COMPRESSION_ALGORITHM].entry->value_unsigned)); 916 } 917 918 dsreg = req_status_items[MT_DSREG].entry->value_signed; 919 if (dsreg != MTIO_DSREG_NIL) { 920 const char sfmt[] = "Current Driver State: %s.\n"; 921 printf("---------------------------------\n"); 922 const char *state_str; 923 924 state_str = get_driver_state_str(dsreg); 925 if (state_str == NULL) { 926 char foo[32]; 927 (void) sprintf(foo, "Unknown state 0x%x", dsreg); 928 printf(sfmt, foo); 929 } else { 930 printf(sfmt, state_str); 931 } 932 } 933 resid = req_status_items[MT_RESID].entry->value_signed; 934 calculated_fileno = req_status_items[ 935 MT_CALCULATED_FILENO].entry->value_signed; 936 calculated_rel_blkno = req_status_items[ 937 MT_CALCULATED_REL_BLKNO].entry->value_signed; 938 rep_fileno = req_status_items[ 939 MT_REPORTED_FILENO].entry->value_signed; 940 rep_blkno = req_status_items[ 941 MT_REPORTED_BLKNO].entry->value_signed; 942 bop = req_status_items[MT_BOP].entry->value_signed; 943 eop = req_status_items[MT_EOP].entry->value_signed; 944 bpew = req_status_items[MT_BPEW].entry->value_signed; 945 partition = req_status_items[MT_PARTITION].entry->value_signed; 946 947 printf("---------------------------------\n"); 948 printf("Partition: %3jd Calc File Number: %3jd " 949 " Calc Record Number: %jd\n" 950 "Residual: %3jd Reported File Number: %3jd " 951 "Reported Record Number: %jd\n", partition, calculated_fileno, 952 calculated_rel_blkno, resid, rep_fileno, rep_blkno); 953 954 printf("Flags: "); 955 if (bop > 0 || eop > 0 || bpew > 0) { 956 int need_comma = 0; 957 958 if (bop > 0) { 959 printf("BOP"); 960 need_comma = 1; 961 } 962 if (eop > 0) { 963 if (need_comma != 0) 964 printf(","); 965 printf("EOP"); 966 need_comma = 1; 967 } 968 if (bpew > 0) { 969 if (need_comma != 0) 970 printf(","); 971 printf("BPEW"); 972 need_comma = 1; 973 } 974 } else { 975 printf("None"); 976 } 977 printf("\n"); 978 if (verbose != 0) { 979 printf("---------------------------------\n"); 980 printf("Tape I/O parameters:\n"); 981 for (i = MT_MAXIO; i <= MT_MAX_EFF_IOSIZE; i++) { 982 printf(" %s (%s): %ju bytes\n", 983 req_status_items[i].entry->desc, 984 req_status_items[i].name, 985 req_status_items[i].entry->value_unsigned); 986 } 987 } 988 989 return (0); 990 } 991 992 int 993 mt_xml_cmd(unsigned long cmd, int argc, char **argv, int mtfd, const char *tape) 994 { 995 struct mt_status_data status_data; 996 #if 0 997 struct mt_status_entry *entry; 998 #endif 999 char *xml_str; 1000 int retval; 1001 unsigned long ioctl_cmd; 1002 1003 switch (cmd) { 1004 case MT_CMD_PROTECT: 1005 case MTIOCPARAMGET: 1006 ioctl_cmd = MTIOCPARAMGET; 1007 break; 1008 default: 1009 ioctl_cmd = MTIOCEXTGET; 1010 break; 1011 } 1012 1013 retval = mt_get_xml_str(mtfd, ioctl_cmd, &xml_str); 1014 if (retval != 0) 1015 err(1, "Couldn't get mt XML string"); 1016 1017 retval = mt_get_status(xml_str, &status_data); 1018 if (retval != XML_STATUS_OK) { 1019 warn("Couldn't get mt status for %s", tape); 1020 goto bailout; 1021 } 1022 1023 /* 1024 * This gets set if there are memory allocation or other errors in 1025 * our parsing of the XML. 1026 */ 1027 if (status_data.error != 0) { 1028 warnx("%s", status_data.error_str); 1029 retval = 1; 1030 goto bailout; 1031 } 1032 #if 0 1033 STAILQ_FOREACH(entry, &status_data.entries, links) 1034 mt_status_tree_print(entry, 0, NULL); 1035 #endif 1036 1037 switch (cmd) { 1038 case MTIOCEXTGET: 1039 retval = nstatus_print(argc, argv, xml_str, &status_data); 1040 break; 1041 case MTIOCPARAMGET: 1042 retval = mt_param(argc, argv, mtfd, xml_str, &status_data); 1043 break; 1044 case MT_CMD_PROTECT: 1045 retval = mt_protect(argc, argv, mtfd, &status_data); 1046 break; 1047 case MT_CMD_GETDENSITY: 1048 retval = mt_getdensity(argc, argv, xml_str, &status_data); 1049 break; 1050 } 1051 1052 bailout: 1053 if (xml_str != NULL) 1054 free(xml_str); 1055 1056 mt_status_free(&status_data); 1057 1058 return (retval); 1059 } 1060 1061 static int 1062 mt_set_param(int mtfd, struct mt_status_data *status_data, char *param_name, 1063 char *param_value) 1064 { 1065 struct mt_status_entry *entry; 1066 struct mtparamset param_set; 1067 1068 entry = mt_status_entry_find(status_data, 1069 __DECONST(char *, "mtparamget")); 1070 if (entry == NULL) 1071 errx(1, "Cannot find parameter root node"); 1072 1073 bzero(¶m_set, sizeof(param_set)); 1074 entry = mt_entry_find(entry, param_name); 1075 if (entry == NULL) 1076 errx(1, "Unknown parameter name \"%s\"", param_name); 1077 1078 strlcpy(param_set.value_name, param_name, sizeof(param_set.value_name)); 1079 1080 switch (entry->var_type) { 1081 case MT_TYPE_INT: 1082 param_set.value.value_signed = strtoll(param_value, NULL, 0); 1083 param_set.value_type = MT_PARAM_SET_SIGNED; 1084 param_set.value_len = entry->size; 1085 break; 1086 case MT_TYPE_UINT: 1087 param_set.value.value_unsigned = strtoull(param_value, NULL, 0); 1088 param_set.value_type = MT_PARAM_SET_UNSIGNED; 1089 param_set.value_len = entry->size; 1090 break; 1091 case MT_TYPE_STRING: { 1092 size_t param_len; 1093 1094 param_len = strlen(param_value) + 1; 1095 if (param_len > sizeof(param_set.value.value_fixed_str)) { 1096 param_set.value_type = MT_PARAM_SET_VAR_STR; 1097 param_set.value.value_var_str = param_value; 1098 } else { 1099 param_set.value_type = MT_PARAM_SET_FIXED_STR; 1100 strlcpy(param_set.value.value_fixed_str, param_value, 1101 sizeof(param_set.value.value_fixed_str)); 1102 } 1103 param_set.value_len = param_len; 1104 break; 1105 } 1106 default: 1107 errx(1, "Unknown parameter type %d for %s", entry->var_type, 1108 param_name); 1109 break; 1110 } 1111 1112 if (ioctl(mtfd, MTIOCPARAMSET, ¶m_set) == -1) 1113 err(1, "MTIOCPARAMSET"); 1114 1115 if (param_set.status != MT_PARAM_STATUS_OK) 1116 errx(1, "Failed to set %s: %s", param_name, 1117 param_set.error_str); 1118 1119 return (0); 1120 } 1121 1122 1123 typedef enum { 1124 MT_PP_LBP_R, 1125 MT_PP_LBP_W, 1126 MT_PP_RBDP, 1127 MT_PP_PI_LENGTH, 1128 MT_PP_PROT_METHOD 1129 } mt_protect_param; 1130 1131 static struct mt_protect_info { 1132 const char *name; 1133 struct mt_status_entry *entry; 1134 uint32_t value; 1135 } mt_protect_list[] = { 1136 { "lbp_r", NULL, 0 }, 1137 { "lbp_w", NULL, 0 }, 1138 { "rbdp", NULL, 0 }, 1139 { "pi_length", NULL, 0 }, 1140 { "prot_method", NULL, 0 } 1141 }; 1142 1143 #define MT_NUM_PROTECT_PARAMS (sizeof(mt_protect_list)/sizeof(mt_protect_list[0])) 1144 1145 #define MT_PROT_NAME "protection" 1146 1147 static int 1148 mt_protect(int argc, char **argv, int mtfd, struct mt_status_data *status_data) 1149 { 1150 int retval = 0; 1151 int do_enable = 0, do_disable = 0, do_list = 0; 1152 int rbdp_set = 0, lbp_w_set = 0, lbp_r_set = 0; 1153 int prot_method_set = 0, pi_length_set = 0; 1154 int verbose = 0; 1155 uint32_t rbdp = 0, lbp_w = 0, lbp_r = 0; 1156 uint32_t prot_method = 0, pi_length = 0; 1157 struct mt_status_entry *prot_entry, *supported_entry; 1158 struct mt_status_entry *entry; 1159 struct mtparamset params[MT_NUM_PROTECT_PARAMS]; 1160 struct mtsetlist param_list; 1161 unsigned int i; 1162 int c; 1163 1164 while ((c = getopt(argc, argv, "b:delL:m:r:vw:")) != -1) { 1165 switch (c) { 1166 case 'b': 1167 rbdp_set = 1; 1168 rbdp = strtoul(optarg, NULL, 0); 1169 if ((rbdp != 0) && (rbdp != 1)) 1170 errx(1, "valid values for -b are 0 and 1"); 1171 break; 1172 case 'd': 1173 do_disable = 1; 1174 break; 1175 case 'e': 1176 do_enable = 1; 1177 break; 1178 case 'l': 1179 do_list = 1; 1180 break; 1181 case 'L': 1182 pi_length_set = 1; 1183 pi_length = strtoul(optarg, NULL, 0); 1184 if (pi_length > SA_CTRL_DP_PI_LENGTH_MASK) 1185 errx(1, "PI length %u > maximum %u", 1186 pi_length, SA_CTRL_DP_PI_LENGTH_MASK); 1187 break; 1188 case 'm': 1189 prot_method_set = 1; 1190 prot_method = strtoul(optarg, NULL, 0); 1191 if (prot_method > SA_CTRL_DP_METHOD_MAX) 1192 errx(1, "Method %u > maximum %u", 1193 prot_method, SA_CTRL_DP_METHOD_MAX); 1194 break; 1195 case 'r': 1196 lbp_r_set = 1; 1197 lbp_r = strtoul(optarg, NULL, 0); 1198 if ((lbp_r != 0) && (lbp_r != 1)) 1199 errx(1, "valid values for -r are 0 and 1"); 1200 break; 1201 case 'v': 1202 verbose = 1; 1203 break; 1204 case 'w': 1205 lbp_w_set = 1; 1206 lbp_w = strtoul(optarg, NULL, 0); 1207 if ((lbp_w != 0) && (lbp_r != 1)) 1208 errx(1, "valid values for -r are 0 and 1"); 1209 break; 1210 default: 1211 break; 1212 } 1213 } 1214 1215 if ((rbdp_set + do_disable + do_enable + do_list + pi_length_set + 1216 prot_method_set + lbp_r_set + lbp_w_set) == 0) 1217 errx(1, "Need an argument for protect"); 1218 1219 if ((do_disable + do_enable + do_list) != 1) 1220 errx(1, "You must specify only one of -e, -d or -l"); 1221 1222 if (do_list != 0) { 1223 retval = mt_protect_print(status_data, verbose); 1224 goto bailout; 1225 } 1226 if (do_enable != 0) { 1227 /* 1228 * Enable protection, but allow the user to override 1229 * settings if he doesn't want everything turned on. 1230 */ 1231 if (rbdp_set == 0) 1232 rbdp = 1; 1233 if (lbp_w_set == 0) 1234 lbp_w = 1; 1235 if (lbp_r_set == 0) 1236 lbp_r = 1; 1237 /* 1238 * If the user doesn't override it, we default to enabling 1239 * Reed-Solomon checkums. 1240 */ 1241 if (prot_method_set == 0) 1242 prot_method = SA_CTRL_DP_REED_SOLOMON; 1243 if (pi_length_set == 0) 1244 pi_length = SA_CTRL_DP_RS_LENGTH; 1245 } else if (do_disable != 0) { 1246 /* 1247 * If the user wants to disable protection, we ignore any 1248 * other parameters he has set. Everything gets set to 0. 1249 */ 1250 rbdp = lbp_w = lbp_r = 0; 1251 prot_method = pi_length = 0; 1252 } 1253 1254 prot_entry = mt_status_entry_find(status_data, 1255 __DECONST(char *, MT_PROT_NAME)); 1256 if (prot_entry == NULL) 1257 errx(1, "Unable to find protection information status"); 1258 1259 supported_entry = mt_entry_find(prot_entry, 1260 __DECONST(char *, "protection_supported")); 1261 if (supported_entry == NULL) 1262 errx(1, "Unable to find protection support information"); 1263 1264 if (((supported_entry->var_type == MT_TYPE_INT) 1265 && (supported_entry->value_signed == 0)) 1266 || ((supported_entry->var_type == MT_TYPE_UINT) 1267 && (supported_entry->value_unsigned == 0))) 1268 errx(1, "This device does not support protection information"); 1269 1270 mt_protect_list[MT_PP_LBP_R].value = lbp_r; 1271 mt_protect_list[MT_PP_LBP_W].value = lbp_w; 1272 mt_protect_list[MT_PP_RBDP].value = rbdp; 1273 mt_protect_list[MT_PP_PI_LENGTH].value = pi_length; 1274 mt_protect_list[MT_PP_PROT_METHOD].value = prot_method; 1275 1276 bzero(¶ms, sizeof(params)); 1277 bzero(¶m_list, sizeof(param_list)); 1278 1279 /* 1280 * Go through the list and make sure that we have this parameter, 1281 * and that it is still an unsigned integer. If not, we've got a 1282 * problem. 1283 */ 1284 for (i = 0; i < MT_NUM_PROTECT_PARAMS; i++) { 1285 entry = mt_entry_find(prot_entry, 1286 __DECONST(char *, mt_protect_list[i].name)); 1287 if (entry == NULL) { 1288 errx(1, "Unable to find parameter %s", 1289 mt_protect_list[i].name); 1290 } 1291 mt_protect_list[i].entry = entry; 1292 1293 if (entry->var_type != MT_TYPE_UINT) 1294 errx(1, "Parameter %s is type %d, not unsigned, " 1295 "cannot proceed", mt_protect_list[i].name, 1296 entry->var_type); 1297 snprintf(params[i].value_name, sizeof(params[i].value_name), 1298 "%s.%s", MT_PROT_NAME, mt_protect_list[i].name); 1299 /* XXX KDM unify types here */ 1300 params[i].value_type = MT_PARAM_SET_UNSIGNED; 1301 params[i].value_len = sizeof(mt_protect_list[i].value); 1302 params[i].value.value_unsigned = mt_protect_list[i].value; 1303 1304 } 1305 param_list.num_params = MT_NUM_PROTECT_PARAMS; 1306 param_list.param_len = sizeof(params); 1307 param_list.params = params; 1308 1309 if (ioctl(mtfd, MTIOCSETLIST, ¶m_list) == -1) 1310 err(1, "error issuing MTIOCSETLIST ioctl"); 1311 1312 for (i = 0; i < MT_NUM_PROTECT_PARAMS; i++) { 1313 if (params[i].status != MT_PARAM_STATUS_OK) { 1314 warnx("%s", params[i].error_str); 1315 retval = 1; 1316 } 1317 } 1318 bailout: 1319 1320 return (retval); 1321 } 1322 1323 static int 1324 mt_param(int argc, char **argv, int mtfd, char *xml_str, 1325 struct mt_status_data *status_data) 1326 { 1327 int list = 0, do_set = 0, xml_dump = 0; 1328 char *param_name = NULL, *param_value = NULL; 1329 int retval = 0, quiet = 0; 1330 int c; 1331 1332 while ((c = getopt(argc, argv, "lp:qs:x")) != -1) { 1333 switch (c) { 1334 case 'l': 1335 list = 1; 1336 break; 1337 case 'p': 1338 if (param_name != NULL) { 1339 warnx("Only one paramter name may be " 1340 "specified"); 1341 retval = 1; 1342 goto bailout; 1343 } 1344 param_name = strdup(optarg); 1345 break; 1346 case 'q': 1347 quiet = 1; 1348 break; 1349 case 's': 1350 if (param_value != NULL) { 1351 warnx("Only one paramter value may be " 1352 "specified"); 1353 retval = 1; 1354 goto bailout; 1355 } 1356 param_value = strdup(optarg); 1357 do_set = 1; 1358 break; 1359 case 'x': 1360 xml_dump = 1; 1361 break; 1362 default: 1363 break; 1364 } 1365 } 1366 1367 if ((list + do_set + xml_dump) != 1) { 1368 warnx("You must specify only one of -s, -l or -x"); 1369 retval = 1; 1370 goto bailout; 1371 } 1372 1373 if (xml_dump != 0) { 1374 printf("%s", xml_str); 1375 retval = 0; 1376 goto bailout; 1377 } 1378 1379 if (do_set != 0) { 1380 if (param_name == NULL) 1381 errx(1, "You must specify -p with -s"); 1382 1383 retval = mt_set_param(mtfd, status_data, param_name, 1384 param_value); 1385 } else if (list != 0) 1386 retval = mt_param_list(status_data, param_name, quiet); 1387 1388 bailout: 1389 free(param_name); 1390 free(param_value); 1391 return (retval); 1392 } 1393 1394 int 1395 mt_print_density_entry(struct mt_status_entry *density_root, int indent) 1396 { 1397 struct mt_status_entry *entry; 1398 int retval = 0; 1399 1400 STAILQ_FOREACH(entry, &density_root->child_entries, links) { 1401 if (entry->var_type == MT_TYPE_NODE) { 1402 retval = mt_print_density_entry(entry, indent + 2); 1403 if (retval != 0) 1404 break; 1405 else 1406 continue; 1407 } 1408 if ((strcmp(entry->entry_name, "primary_density_code") == 0) 1409 || (strcmp(entry->entry_name, "secondary_density_code") == 0)){ 1410 1411 /* XXX KDM this should really be unsigned */ 1412 printf("%*s%s (%s): %s\n", indent, "", entry->desc ? 1413 entry->desc : "", entry->entry_name, 1414 denstostring(entry->value_unsigned)); 1415 } else if (strcmp(entry->entry_name, "density_flags") == 0) { 1416 printf("%*sMedium Access: ", indent, ""); 1417 if (entry->value_unsigned & MT_DENS_WRITE_OK) { 1418 printf("Read and Write\n"); 1419 } else { 1420 printf("Read Only\n"); 1421 } 1422 printf("%*sDefault Density: %s\n", indent, "", 1423 (entry->value_unsigned & MT_DENS_DEFLT) ? "Yes" : 1424 "No"); 1425 printf("%*sDuplicate Density: %s\n", indent, "", 1426 (entry->value_unsigned & MT_DENS_DUP) ? "Yes" : 1427 "No"); 1428 } else if (strcmp(entry->entry_name, "media_width") == 0) { 1429 printf("%*s%s (%s): %.1f mm\n", indent, "", 1430 entry->desc ? entry->desc : "", entry->entry_name, 1431 (double)((double)entry->value_unsigned / 10)); 1432 } else if (strcmp(entry->entry_name, "medium_length") == 0) { 1433 printf("%*s%s (%s): %ju m\n", indent, "", 1434 entry->desc ? entry->desc : "", entry->entry_name, 1435 (uintmax_t)entry->value_unsigned); 1436 } else if (strcmp(entry->entry_name, "capacity") == 0) { 1437 printf("%*s%s (%s): %ju MB\n", indent, "", entry->desc ? 1438 entry->desc : "", entry->entry_name, 1439 (uintmax_t)entry->value_unsigned); 1440 } else { 1441 printf("%*s%s (%s): %s\n", indent, "", entry->desc ? 1442 entry->desc : "", entry->entry_name, entry->value); 1443 } 1444 } 1445 1446 return (retval); 1447 } 1448 1449 int 1450 mt_print_density_report(struct mt_status_entry *report_root, int indent) 1451 { 1452 struct mt_status_entry *mt_report, *media_report; 1453 struct mt_status_entry *entry; 1454 int retval = 0; 1455 1456 mt_report = mt_entry_find(report_root, 1457 __DECONST(char *, MT_MEDIUM_TYPE_REPORT_NAME)); 1458 if (mt_report == NULL) 1459 return (1); 1460 1461 media_report = mt_entry_find(report_root, 1462 __DECONST(char *, MT_MEDIA_REPORT_NAME)); 1463 if (media_report == NULL) 1464 return (1); 1465 1466 if ((mt_report->value_signed == 0) 1467 && (media_report->value_signed == 0)) { 1468 printf("%*sThis tape drive supports the following " 1469 "media densities:\n", indent, ""); 1470 } else if ((mt_report->value_signed == 0) 1471 && (media_report->value_signed != 0)) { 1472 printf("%*sThe tape currently in this drive supports " 1473 "the following media densities:\n", indent, ""); 1474 } else if ((mt_report->value_signed != 0) 1475 && (media_report->value_signed == 0)) { 1476 printf("%*sThis tape drive supports the following " 1477 "media types:\n", indent, ""); 1478 } else { 1479 printf("%*sThis tape currently in this drive supports " 1480 "the following media types:\n", indent, ""); 1481 } 1482 1483 STAILQ_FOREACH(entry, &report_root->child_entries, links) { 1484 struct mt_status_nv *nv; 1485 1486 if (strcmp(entry->entry_name, MT_DENSITY_ENTRY_NAME) != 0) 1487 continue; 1488 1489 STAILQ_FOREACH(nv, &entry->nv_list, links) { 1490 if (strcmp(nv->name, "num") != 0) 1491 continue; 1492 1493 break; 1494 } 1495 1496 indent += 2; 1497 1498 printf("%*sDensity Entry", indent, ""); 1499 if (nv != NULL) 1500 printf(" %s", nv->value); 1501 printf(":\n"); 1502 1503 retval = mt_print_density_entry(entry, indent + 2); 1504 1505 indent -= 2; 1506 } 1507 1508 return (retval); 1509 } 1510 1511 int 1512 mt_print_density(struct mt_status_entry *density_root, int indent) 1513 { 1514 struct mt_status_entry *entry; 1515 int retval = 0; 1516 1517 /* 1518 * We should have this entry for every tape drive. This particular 1519 * value is reported via the mode page block header, not the 1520 * SCSI REPORT DENSITY SUPPORT command. 1521 */ 1522 entry = mt_entry_find(density_root, 1523 __DECONST(char *, MT_MEDIA_DENSITY_NAME)); 1524 if (entry == NULL) 1525 errx(1, "Unable to find node %s", MT_MEDIA_DENSITY_NAME); 1526 1527 printf("%*sCurrent density: %s\n", indent, "", 1528 denstostring(entry->value_unsigned)); 1529 1530 /* 1531 * It isn't an error if we don't have any density reports. Tape 1532 * drives that don't support the REPORT DENSITY SUPPORT command 1533 * won't have any; they will only have the current density entry 1534 * above. 1535 */ 1536 STAILQ_FOREACH(entry, &density_root->child_entries, links) { 1537 if (strcmp(entry->entry_name, MT_DENSITY_REPORT_NAME) != 0) 1538 continue; 1539 1540 retval = mt_print_density_report(entry, indent); 1541 } 1542 1543 return (retval); 1544 } 1545 1546 int 1547 mt_getdensity(int argc, char **argv, char *xml_str, 1548 struct mt_status_data *status_data) 1549 { 1550 int retval = 0; 1551 int verbose = 0, xml_dump = 0; 1552 struct mt_status_entry *density_root = NULL; 1553 int c; 1554 1555 while ((c = getopt(argc, argv, "vx")) != -1) { 1556 switch (c) { 1557 case 'v': 1558 verbose = 1; 1559 break; 1560 case 'x': 1561 xml_dump = 1; 1562 break; 1563 } 1564 } 1565 1566 if (xml_dump != 0) { 1567 printf("%s", xml_str); 1568 return (0); 1569 } 1570 1571 density_root = mt_status_entry_find(status_data, 1572 __DECONST(char *, MT_DENSITY_ROOT_NAME)); 1573 if (density_root == NULL) 1574 errx(1, "Cannot find density root node %s", 1575 MT_DENSITY_ROOT_NAME); 1576 1577 retval = mt_print_density(density_root, 0); 1578 1579 return (retval); 1580 } 1581 1582 static void 1583 warn_eof(void) 1584 { 1585 fprintf(stderr, 1586 "The \"eof\" command has been disabled.\n" 1587 "Use \"weof\" if you really want to write end-of-file marks,\n" 1588 "or \"eom\" if you rather want to skip to the end of " 1589 "recorded medium.\n"); 1590 exit(1); 1591 } 1592