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 85 #include <ctype.h> 86 #include <err.h> 87 #include <fcntl.h> 88 #include <stdio.h> 89 #include <stdlib.h> 90 #include <string.h> 91 #include <unistd.h> 92 #include <stdint.h> 93 #include <errno.h> 94 #include <bsdxml.h> 95 #include <mtlib.h> 96 97 #include <cam/cam.h> 98 #include <cam/cam_ccb.h> 99 #include <cam/cam_periph.h> 100 #include <cam/scsi/scsi_all.h> 101 #include <cam/scsi/scsi_sa.h> 102 103 /* the appropriate sections of <sys/mtio.h> are also #ifdef'd for FreeBSD */ 104 /* c_flags */ 105 #define NEED_2ARGS 0x01 106 #define ZERO_ALLOWED 0x02 107 #define IS_DENSITY 0x04 108 #define DISABLE_THIS 0x08 109 #define IS_COMP 0x10 110 #define USE_GETOPT 0x20 111 112 #ifndef TRUE 113 #define TRUE 1 114 #endif 115 #ifndef FALSE 116 #define FALSE 0 117 #endif 118 #ifndef MAX 119 #define MAX(a, b) (a > b) ? a : b 120 #endif 121 #define MT_PLURAL(a) (a == 1) ? "" : "s" 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:\n" 388 " min blocksize %u byte%s\n" 389 " max blocksize %u byte%s\n" 390 " granularity %u byte%s\n", 391 tape, rblim.min_block_length, 392 MT_PLURAL(rblim.min_block_length), 393 rblim.max_block_length, 394 MT_PLURAL(rblim.max_block_length), 395 (1 << rblim.granularity), 396 MT_PLURAL((1 << rblim.granularity))); 397 exit(0); 398 /* NOTREACHED */ 399 } 400 case MTIOCPARAMGET: 401 case MTIOCEXTGET: 402 case MT_CMD_PROTECT: 403 case MT_CMD_GETDENSITY: 404 { 405 int retval = 0; 406 407 retval = mt_xml_cmd(comp->c_code, argc, argv, mtfd, 408 tape); 409 410 exit(retval); 411 } 412 case MTIOCEXTLOCATE: 413 { 414 int retval = 0; 415 416 retval = mt_locate(argc, argv, mtfd, tape); 417 418 exit(retval); 419 } 420 default: 421 break; 422 } 423 if (ioctl(mtfd, MTIOCTOP, &mt_com) < 0) 424 err(1, "%s: %s", tape, comp->c_name); 425 } else { 426 if (ioctl(mtfd, MTIOCGET, &mt_status) < 0) 427 err(1, NULL); 428 status(&mt_status); 429 } 430 exit(0); 431 /* NOTREACHED */ 432 } 433 434 static const struct tape_desc { 435 short t_type; /* type of magtape device */ 436 const char *t_name; /* printing name */ 437 const char *t_dsbits; /* "drive status" register */ 438 const char *t_erbits; /* "error" register */ 439 } tapes[] = { 440 { MT_ISAR, "SCSI tape drive", 0, 0 }, 441 { 0, NULL, 0, 0 } 442 }; 443 444 /* 445 * Interpret the status buffer returned 446 */ 447 static void 448 status(struct mtget *bp) 449 { 450 const struct tape_desc *mt; 451 452 for (mt = tapes;; mt++) { 453 if (mt->t_type == 0) { 454 (void)printf("%d: unknown tape drive type\n", 455 bp->mt_type); 456 return; 457 } 458 if (mt->t_type == bp->mt_type) 459 break; 460 } 461 if(mt->t_type == MT_ISAR) 462 st_status(bp); 463 else { 464 (void)printf("%s tape drive, residual=%d\n", 465 mt->t_name, bp->mt_resid); 466 printreg("ds", (unsigned short)bp->mt_dsreg, mt->t_dsbits); 467 printreg("\ner", (unsigned short)bp->mt_erreg, mt->t_erbits); 468 (void)putchar('\n'); 469 } 470 } 471 472 /* 473 * Print a register a la the %b format of the kernel's printf. 474 */ 475 static void 476 printreg(const char *s, u_int v, const char *bits) 477 { 478 int i, any = 0; 479 char c; 480 481 if (bits && *bits == 8) 482 printf("%s=%o", s, v); 483 else 484 printf("%s=%x", s, v); 485 if (!bits) 486 return; 487 bits++; 488 if (v && bits) { 489 putchar('<'); 490 while ((i = *bits++)) { 491 if (v & (1 << (i-1))) { 492 if (any) 493 putchar(','); 494 any = 1; 495 for (; (c = *bits) > 32; bits++) 496 putchar(c); 497 } else 498 for (; *bits > 32; bits++) 499 ; 500 } 501 putchar('>'); 502 } 503 } 504 505 static void 506 usage(void) 507 { 508 (void)fprintf(stderr, "usage: mt [-f device] command [count]\n"); 509 exit(1); 510 } 511 512 static const struct compression_types { 513 u_int32_t comp_number; 514 const char *name; 515 } comp_types[] = { 516 { 0x00, "none" }, 517 { 0x00, "off" }, 518 { 0x10, "IDRC" }, 519 { 0x20, "DCLZ" }, 520 { 0xffffffff, "enable" }, 521 { 0xffffffff, "on" }, 522 { 0xf0f0f0f0, NULL} 523 }; 524 525 static const char * 526 denstostring(int d) 527 { 528 static char buf[20]; 529 const char *name = mt_density_name(d); 530 531 if (name == NULL) 532 sprintf(buf, "0x%02x", d); 533 else 534 sprintf(buf, "0x%02x:%s", d, name); 535 return buf; 536 } 537 538 static const char * 539 getblksiz(int bs) 540 { 541 static char buf[25]; 542 if (bs == 0) 543 return "variable"; 544 else { 545 sprintf(buf, "%d bytes", bs); 546 return buf; 547 } 548 } 549 550 static const char * 551 comptostring(u_int32_t comp) 552 { 553 static char buf[20]; 554 const struct compression_types *ct; 555 556 if (comp == MT_COMP_DISABLED) 557 return "disabled"; 558 else if (comp == MT_COMP_UNSUPP) 559 return "unsupported"; 560 561 for (ct = comp_types; ct->name; ct++) 562 if (ct->comp_number == comp) 563 break; 564 565 if (ct->comp_number == 0xf0f0f0f0) { 566 sprintf(buf, "0x%x", comp); 567 return(buf); 568 } else 569 return(ct->name); 570 } 571 572 static u_int32_t 573 stringtocomp(const char *s) 574 { 575 const struct compression_types *ct; 576 size_t l = strlen(s); 577 578 for (ct = comp_types; ct->name; ct++) 579 if (strncasecmp(ct->name, s, l) == 0) 580 break; 581 582 return(ct->comp_number); 583 } 584 585 static struct driver_state { 586 int dsreg; 587 const char *desc; 588 } driver_states[] = { 589 { MTIO_DSREG_REST, "at rest" }, 590 { MTIO_DSREG_RBSY, "Communicating with drive" }, 591 { MTIO_DSREG_WR, "Writing" }, 592 { MTIO_DSREG_FMK, "Writing Filemarks" }, 593 { MTIO_DSREG_ZER, "Erasing" }, 594 { MTIO_DSREG_RD, "Reading" }, 595 { MTIO_DSREG_FWD, "Spacing Forward" }, 596 { MTIO_DSREG_REV, "Spacing Reverse" }, 597 { MTIO_DSREG_POS, "Hardware Positioning (direction unknown)" }, 598 { MTIO_DSREG_REW, "Rewinding" }, 599 { MTIO_DSREG_TEN, "Retensioning" }, 600 { MTIO_DSREG_UNL, "Unloading" }, 601 { MTIO_DSREG_LD, "Loading" }, 602 }; 603 604 const char * 605 get_driver_state_str(int dsreg) 606 { 607 unsigned int i; 608 609 for (i = 0; i < (sizeof(driver_states)/sizeof(driver_states[0])); i++) { 610 if (driver_states[i].dsreg == dsreg) 611 return (driver_states[i].desc); 612 } 613 614 return (NULL); 615 } 616 617 static void 618 st_status(struct mtget *bp) 619 { 620 printf("Mode Density Blocksize bpi " 621 "Compression\n" 622 "Current: %-17s %-12s %-7d %s\n" 623 "---------available modes---------\n" 624 "0: %-17s %-12s %-7d %s\n" 625 "1: %-17s %-12s %-7d %s\n" 626 "2: %-17s %-12s %-7d %s\n" 627 "3: %-17s %-12s %-7d %s\n", 628 denstostring(bp->mt_density), getblksiz(bp->mt_blksiz), 629 mt_density_bp(bp->mt_density, TRUE), comptostring(bp->mt_comp), 630 denstostring(bp->mt_density0), getblksiz(bp->mt_blksiz0), 631 mt_density_bp(bp->mt_density0, TRUE), comptostring(bp->mt_comp0), 632 denstostring(bp->mt_density1), getblksiz(bp->mt_blksiz1), 633 mt_density_bp(bp->mt_density1, TRUE), comptostring(bp->mt_comp1), 634 denstostring(bp->mt_density2), getblksiz(bp->mt_blksiz2), 635 mt_density_bp(bp->mt_density2, TRUE), comptostring(bp->mt_comp2), 636 denstostring(bp->mt_density3), getblksiz(bp->mt_blksiz3), 637 mt_density_bp(bp->mt_density3, TRUE), comptostring(bp->mt_comp3)); 638 639 if (bp->mt_dsreg != MTIO_DSREG_NIL) { 640 const char sfmt[] = "Current Driver State: %s.\n"; 641 printf("---------------------------------\n"); 642 const char *state_str; 643 644 state_str = get_driver_state_str(bp->mt_dsreg); 645 if (state_str == NULL) { 646 char foo[32]; 647 (void) sprintf(foo, "Unknown state 0x%x", bp->mt_dsreg); 648 printf(sfmt, foo); 649 } else { 650 printf(sfmt, state_str); 651 } 652 } 653 if (bp->mt_resid == 0 && bp->mt_fileno == (daddr_t) -1 && 654 bp->mt_blkno == (daddr_t) -1) 655 return; 656 printf("---------------------------------\n"); 657 printf("File Number: %d\tRecord Number: %d\tResidual Count %d\n", 658 bp->mt_fileno, bp->mt_blkno, bp->mt_resid); 659 } 660 661 static int 662 mt_locate(int argc, char **argv, int mtfd, const char *tape) 663 { 664 struct mtlocate mtl; 665 uint64_t logical_id = 0; 666 mt_locate_dest_type dest_type = MT_LOCATE_DEST_FILE; 667 int eod = 0, explicit = 0, immediate = 0; 668 int64_t partition = 0; 669 int block_addr_set = 0, partition_set = 0, file_set = 0, set_set = 0; 670 int c, retval; 671 672 retval = 0; 673 bzero(&mtl, sizeof(mtl)); 674 675 while ((c = getopt(argc, argv, "b:eEf:ip:s:")) != -1) { 676 switch (c) { 677 case 'b': 678 /* Block address */ 679 logical_id = strtoull(optarg, NULL, 0); 680 dest_type = MT_LOCATE_DEST_OBJECT; 681 block_addr_set = 1; 682 break; 683 case 'e': 684 /* end of data */ 685 eod = 1; 686 dest_type = MT_LOCATE_DEST_EOD; 687 break; 688 case 'E': 689 /* 690 * XXX KDM explicit address mode. Should we even 691 * allow this, since the driver doesn't operate in 692 * explicit address mode? 693 */ 694 explicit = 1; 695 break; 696 case 'f': 697 /* file number */ 698 logical_id = strtoull(optarg, NULL, 0); 699 dest_type = MT_LOCATE_DEST_FILE; 700 file_set = 1; 701 break; 702 case 'i': 703 /* 704 * Immediate address mode. XXX KDM do we want to 705 * implement this? The other commands in the 706 * tape driver will need to be able to handle this. 707 */ 708 immediate = 1; 709 break; 710 case 'p': 711 /* 712 * Change partition to the given partition. 713 */ 714 partition = strtol(optarg, NULL, 0); 715 partition_set = 1; 716 break; 717 case 's': 718 /* Go to the given set mark */ 719 logical_id = strtoull(optarg, NULL, 0); 720 dest_type = MT_LOCATE_DEST_SET; 721 set_set = 1; 722 break; 723 default: 724 break; 725 } 726 } 727 728 /* 729 * These options are mutually exclusive. The user may only specify 730 * one. 731 */ 732 if ((block_addr_set + file_set + eod + set_set) != 1) 733 errx(1, "You must specify only one of -b, -f, -e, or -s"); 734 735 mtl.dest_type = dest_type; 736 switch (dest_type) { 737 case MT_LOCATE_DEST_OBJECT: 738 case MT_LOCATE_DEST_FILE: 739 case MT_LOCATE_DEST_SET: 740 mtl.logical_id = logical_id; 741 break; 742 case MT_LOCATE_DEST_EOD: 743 break; 744 } 745 746 if (immediate != 0) 747 mtl.flags |= MT_LOCATE_FLAG_IMMED; 748 749 if (partition_set != 0) { 750 mtl.flags |= MT_LOCATE_FLAG_CHANGE_PART; 751 mtl.partition = partition; 752 } 753 754 if (explicit != 0) 755 mtl.block_address_mode = MT_LOCATE_BAM_EXPLICIT; 756 else 757 mtl.block_address_mode = MT_LOCATE_BAM_IMPLICIT; 758 759 if (ioctl(mtfd, MTIOCEXTLOCATE, &mtl) == -1) 760 err(1, "MTIOCEXTLOCATE ioctl failed on %s", tape); 761 762 return (retval); 763 } 764 765 typedef enum { 766 MT_PERIPH_NAME = 0, 767 MT_UNIT_NUMBER = 1, 768 MT_VENDOR = 2, 769 MT_PRODUCT = 3, 770 MT_REVISION = 4, 771 MT_COMPRESSION_SUPPORTED = 5, 772 MT_COMPRESSION_ENABLED = 6, 773 MT_COMPRESSION_ALGORITHM = 7, 774 MT_MEDIA_DENSITY = 8, 775 MT_MEDIA_BLOCKSIZE = 9, 776 MT_CALCULATED_FILENO = 10, 777 MT_CALCULATED_REL_BLKNO = 11, 778 MT_REPORTED_FILENO = 12, 779 MT_REPORTED_BLKNO = 13, 780 MT_PARTITION = 14, 781 MT_BOP = 15, 782 MT_EOP = 16, 783 MT_BPEW = 17, 784 MT_DSREG = 18, 785 MT_RESID = 19, 786 MT_FIXED_MODE = 20, 787 MT_SERIAL_NUM = 21, 788 MT_MAXIO = 22, 789 MT_CPI_MAXIO = 23, 790 MT_MAX_BLK = 24, 791 MT_MIN_BLK = 25, 792 MT_BLK_GRAN = 26, 793 MT_MAX_EFF_IOSIZE = 27 794 } status_item_index; 795 796 static struct mt_status_items { 797 const char *name; 798 struct mt_status_entry *entry; 799 } req_status_items[] = { 800 { "periph_name", NULL }, 801 { "unit_number", NULL }, 802 { "vendor", NULL }, 803 { "product", NULL }, 804 { "revision", NULL }, 805 { "compression_supported", NULL }, 806 { "compression_enabled", NULL }, 807 { "compression_algorithm", NULL }, 808 { "media_density", NULL }, 809 { "media_blocksize", NULL }, 810 { "calculated_fileno", NULL }, 811 { "calculated_rel_blkno", NULL }, 812 { "reported_fileno", NULL }, 813 { "reported_blkno", NULL }, 814 { "partition", NULL }, 815 { "bop", NULL }, 816 { "eop", NULL }, 817 { "bpew", NULL }, 818 { "dsreg", NULL }, 819 { "residual", NULL }, 820 { "fixed_mode", NULL }, 821 { "serial_num", NULL }, 822 { "maxio", NULL }, 823 { "cpi_maxio", NULL }, 824 { "max_blk", NULL }, 825 { "min_blk", NULL }, 826 { "blk_gran", NULL }, 827 { "max_effective_iosize", NULL } 828 }; 829 830 int 831 nstatus_print(int argc, char **argv, char *xml_str, 832 struct mt_status_data *status_data) 833 { 834 unsigned int i; 835 int64_t calculated_fileno, calculated_rel_blkno; 836 int64_t rep_fileno, rep_blkno, partition, resid; 837 char block_str[32]; 838 const char *dens_str; 839 int dsreg, bop, eop, bpew; 840 int xml_dump = 0; 841 size_t dens_len; 842 unsigned int field_width; 843 int verbose = 0; 844 int c; 845 846 while ((c = getopt(argc, argv, "xv")) != -1) { 847 switch (c) { 848 case 'x': 849 xml_dump = 1; 850 break; 851 case 'v': 852 verbose = 1; 853 break; 854 default: 855 break; 856 } 857 } 858 859 if (xml_dump != 0) { 860 printf("%s", xml_str); 861 return (0); 862 } 863 864 for (i = 0; i < (sizeof(req_status_items)/sizeof(req_status_items[0])); 865 i++) { 866 char *name; 867 868 name = __DECONST(char *, req_status_items[i].name); 869 req_status_items[i].entry = mt_status_entry_find(status_data, 870 name); 871 if (req_status_items[i].entry == NULL) { 872 errx(1, "Cannot find status entry %s", 873 req_status_items[i].name); 874 } 875 } 876 877 printf("Drive: %s%ju: <%s %s %s> Serial Number: %s\n", 878 req_status_items[MT_PERIPH_NAME].entry->value, 879 (uintmax_t)req_status_items[MT_UNIT_NUMBER].entry->value_unsigned, 880 req_status_items[MT_VENDOR].entry->value, 881 req_status_items[MT_PRODUCT].entry->value, 882 req_status_items[MT_REVISION].entry->value, 883 (req_status_items[MT_SERIAL_NUM].entry->value) ? 884 req_status_items[MT_SERIAL_NUM].entry->value : "none"); 885 printf("---------------------------------\n"); 886 887 /* 888 * We check to see whether we're in fixed mode or not, and don't 889 * just believe the blocksize. If the SILI bit is turned on, the 890 * blocksize will be set to 4, even though we're doing variable 891 * length (well, multiples of 4) blocks. 892 */ 893 if (req_status_items[MT_FIXED_MODE].entry->value_signed == 0) 894 snprintf(block_str, sizeof(block_str), "variable"); 895 else 896 snprintf(block_str, sizeof(block_str), "%s", 897 getblksiz(req_status_items[ 898 MT_MEDIA_BLOCKSIZE].entry->value_unsigned)); 899 900 dens_str = denstostring(req_status_items[ 901 MT_MEDIA_DENSITY].entry->value_unsigned); 902 if (dens_str == NULL) 903 dens_len = 0; 904 else 905 dens_len = strlen(dens_str); 906 field_width = MAX(dens_len, 17); 907 printf("Mode %-*s Blocksize bpi Compression\n" 908 "Current: %-*s %-12s %-7d ", 909 field_width, "Density", field_width, dens_str, block_str, 910 mt_density_bp(req_status_items[ 911 MT_MEDIA_DENSITY].entry->value_unsigned, TRUE)); 912 913 if (req_status_items[MT_COMPRESSION_SUPPORTED].entry->value_signed == 0) 914 printf("unsupported\n"); 915 else if (req_status_items[ 916 MT_COMPRESSION_ENABLED].entry->value_signed == 0) 917 printf("disabled\n"); 918 else { 919 printf("enabled (%s)\n", 920 comptostring(req_status_items[ 921 MT_COMPRESSION_ALGORITHM].entry->value_unsigned)); 922 } 923 924 dsreg = req_status_items[MT_DSREG].entry->value_signed; 925 if (dsreg != MTIO_DSREG_NIL) { 926 const char sfmt[] = "Current Driver State: %s.\n"; 927 printf("---------------------------------\n"); 928 const char *state_str; 929 930 state_str = get_driver_state_str(dsreg); 931 if (state_str == NULL) { 932 char foo[32]; 933 (void) sprintf(foo, "Unknown state 0x%x", dsreg); 934 printf(sfmt, foo); 935 } else { 936 printf(sfmt, state_str); 937 } 938 } 939 resid = req_status_items[MT_RESID].entry->value_signed; 940 calculated_fileno = req_status_items[ 941 MT_CALCULATED_FILENO].entry->value_signed; 942 calculated_rel_blkno = req_status_items[ 943 MT_CALCULATED_REL_BLKNO].entry->value_signed; 944 rep_fileno = req_status_items[ 945 MT_REPORTED_FILENO].entry->value_signed; 946 rep_blkno = req_status_items[ 947 MT_REPORTED_BLKNO].entry->value_signed; 948 bop = req_status_items[MT_BOP].entry->value_signed; 949 eop = req_status_items[MT_EOP].entry->value_signed; 950 bpew = req_status_items[MT_BPEW].entry->value_signed; 951 partition = req_status_items[MT_PARTITION].entry->value_signed; 952 953 printf("---------------------------------\n"); 954 printf("Partition: %3jd Calc File Number: %3jd " 955 " Calc Record Number: %jd\n" 956 "Residual: %3jd Reported File Number: %3jd " 957 "Reported Record Number: %jd\n", partition, calculated_fileno, 958 calculated_rel_blkno, resid, rep_fileno, rep_blkno); 959 960 printf("Flags: "); 961 if (bop > 0 || eop > 0 || bpew > 0) { 962 int need_comma = 0; 963 964 if (bop > 0) { 965 printf("BOP"); 966 need_comma = 1; 967 } 968 if (eop > 0) { 969 if (need_comma != 0) 970 printf(","); 971 printf("EOP"); 972 need_comma = 1; 973 } 974 if (bpew > 0) { 975 if (need_comma != 0) 976 printf(","); 977 printf("BPEW"); 978 need_comma = 1; 979 } 980 } else { 981 printf("None"); 982 } 983 printf("\n"); 984 if (verbose != 0) { 985 printf("---------------------------------\n"); 986 printf("Tape I/O parameters:\n"); 987 for (i = MT_MAXIO; i <= MT_MAX_EFF_IOSIZE; i++) { 988 printf(" %s (%s): %ju bytes\n", 989 req_status_items[i].entry->desc, 990 req_status_items[i].name, 991 req_status_items[i].entry->value_unsigned); 992 } 993 } 994 995 return (0); 996 } 997 998 int 999 mt_xml_cmd(unsigned long cmd, int argc, char **argv, int mtfd, const char *tape) 1000 { 1001 struct mt_status_data status_data; 1002 #if 0 1003 struct mt_status_entry *entry; 1004 #endif 1005 char *xml_str; 1006 int retval; 1007 unsigned long ioctl_cmd; 1008 1009 switch (cmd) { 1010 case MT_CMD_PROTECT: 1011 case MTIOCPARAMGET: 1012 ioctl_cmd = MTIOCPARAMGET; 1013 break; 1014 default: 1015 ioctl_cmd = MTIOCEXTGET; 1016 break; 1017 } 1018 1019 retval = mt_get_xml_str(mtfd, ioctl_cmd, &xml_str); 1020 if (retval != 0) 1021 err(1, "Couldn't get mt XML string"); 1022 1023 retval = mt_get_status(xml_str, &status_data); 1024 if (retval != XML_STATUS_OK) { 1025 warn("Couldn't get mt status for %s", tape); 1026 goto bailout; 1027 } 1028 1029 /* 1030 * This gets set if there are memory allocation or other errors in 1031 * our parsing of the XML. 1032 */ 1033 if (status_data.error != 0) { 1034 warnx("%s", status_data.error_str); 1035 retval = 1; 1036 goto bailout; 1037 } 1038 #if 0 1039 STAILQ_FOREACH(entry, &status_data.entries, links) 1040 mt_status_tree_print(entry, 0, NULL); 1041 #endif 1042 1043 switch (cmd) { 1044 case MTIOCEXTGET: 1045 retval = nstatus_print(argc, argv, xml_str, &status_data); 1046 break; 1047 case MTIOCPARAMGET: 1048 retval = mt_param(argc, argv, mtfd, xml_str, &status_data); 1049 break; 1050 case MT_CMD_PROTECT: 1051 retval = mt_protect(argc, argv, mtfd, &status_data); 1052 break; 1053 case MT_CMD_GETDENSITY: 1054 retval = mt_getdensity(argc, argv, xml_str, &status_data); 1055 break; 1056 } 1057 1058 bailout: 1059 if (xml_str != NULL) 1060 free(xml_str); 1061 1062 mt_status_free(&status_data); 1063 1064 return (retval); 1065 } 1066 1067 static int 1068 mt_set_param(int mtfd, struct mt_status_data *status_data, char *param_name, 1069 char *param_value) 1070 { 1071 struct mt_status_entry *entry; 1072 struct mtparamset param_set; 1073 1074 entry = mt_status_entry_find(status_data, 1075 __DECONST(char *, "mtparamget")); 1076 if (entry == NULL) 1077 errx(1, "Cannot find parameter root node"); 1078 1079 bzero(¶m_set, sizeof(param_set)); 1080 entry = mt_entry_find(entry, param_name); 1081 if (entry == NULL) 1082 errx(1, "Unknown parameter name \"%s\"", param_name); 1083 1084 strlcpy(param_set.value_name, param_name, sizeof(param_set.value_name)); 1085 1086 switch (entry->var_type) { 1087 case MT_TYPE_INT: 1088 param_set.value.value_signed = strtoll(param_value, NULL, 0); 1089 param_set.value_type = MT_PARAM_SET_SIGNED; 1090 param_set.value_len = entry->size; 1091 break; 1092 case MT_TYPE_UINT: 1093 param_set.value.value_unsigned = strtoull(param_value, NULL, 0); 1094 param_set.value_type = MT_PARAM_SET_UNSIGNED; 1095 param_set.value_len = entry->size; 1096 break; 1097 case MT_TYPE_STRING: { 1098 size_t param_len; 1099 1100 param_len = strlen(param_value) + 1; 1101 if (param_len > sizeof(param_set.value.value_fixed_str)) { 1102 param_set.value_type = MT_PARAM_SET_VAR_STR; 1103 param_set.value.value_var_str = param_value; 1104 } else { 1105 param_set.value_type = MT_PARAM_SET_FIXED_STR; 1106 strlcpy(param_set.value.value_fixed_str, param_value, 1107 sizeof(param_set.value.value_fixed_str)); 1108 } 1109 param_set.value_len = param_len; 1110 break; 1111 } 1112 default: 1113 errx(1, "Unknown parameter type %d for %s", entry->var_type, 1114 param_name); 1115 break; 1116 } 1117 1118 if (ioctl(mtfd, MTIOCPARAMSET, ¶m_set) == -1) 1119 err(1, "MTIOCPARAMSET"); 1120 1121 if (param_set.status != MT_PARAM_STATUS_OK) 1122 errx(1, "Failed to set %s: %s", param_name, 1123 param_set.error_str); 1124 1125 return (0); 1126 } 1127 1128 1129 typedef enum { 1130 MT_PP_LBP_R, 1131 MT_PP_LBP_W, 1132 MT_PP_RBDP, 1133 MT_PP_PI_LENGTH, 1134 MT_PP_PROT_METHOD 1135 } mt_protect_param; 1136 1137 static struct mt_protect_info { 1138 const char *name; 1139 struct mt_status_entry *entry; 1140 uint32_t value; 1141 } mt_protect_list[] = { 1142 { "lbp_r", NULL, 0 }, 1143 { "lbp_w", NULL, 0 }, 1144 { "rbdp", NULL, 0 }, 1145 { "pi_length", NULL, 0 }, 1146 { "prot_method", NULL, 0 } 1147 }; 1148 1149 #define MT_NUM_PROTECT_PARAMS (sizeof(mt_protect_list)/sizeof(mt_protect_list[0])) 1150 1151 #define MT_PROT_NAME "protection" 1152 1153 static int 1154 mt_protect(int argc, char **argv, int mtfd, struct mt_status_data *status_data) 1155 { 1156 int retval = 0; 1157 int do_enable = 0, do_disable = 0, do_list = 0; 1158 int rbdp_set = 0, lbp_w_set = 0, lbp_r_set = 0; 1159 int prot_method_set = 0, pi_length_set = 0; 1160 int verbose = 0; 1161 uint32_t rbdp = 0, lbp_w = 0, lbp_r = 0; 1162 uint32_t prot_method = 0, pi_length = 0; 1163 struct mt_status_entry *prot_entry, *supported_entry; 1164 struct mt_status_entry *entry; 1165 struct mtparamset params[MT_NUM_PROTECT_PARAMS]; 1166 struct mtsetlist param_list; 1167 unsigned int i; 1168 int c; 1169 1170 while ((c = getopt(argc, argv, "b:delL:m:r:vw:")) != -1) { 1171 switch (c) { 1172 case 'b': 1173 rbdp_set = 1; 1174 rbdp = strtoul(optarg, NULL, 0); 1175 if ((rbdp != 0) && (rbdp != 1)) 1176 errx(1, "valid values for -b are 0 and 1"); 1177 break; 1178 case 'd': 1179 do_disable = 1; 1180 break; 1181 case 'e': 1182 do_enable = 1; 1183 break; 1184 case 'l': 1185 do_list = 1; 1186 break; 1187 case 'L': 1188 pi_length_set = 1; 1189 pi_length = strtoul(optarg, NULL, 0); 1190 if (pi_length > SA_CTRL_DP_PI_LENGTH_MASK) 1191 errx(1, "PI length %u > maximum %u", 1192 pi_length, SA_CTRL_DP_PI_LENGTH_MASK); 1193 break; 1194 case 'm': 1195 prot_method_set = 1; 1196 prot_method = strtoul(optarg, NULL, 0); 1197 if (prot_method > SA_CTRL_DP_METHOD_MAX) 1198 errx(1, "Method %u > maximum %u", 1199 prot_method, SA_CTRL_DP_METHOD_MAX); 1200 break; 1201 case 'r': 1202 lbp_r_set = 1; 1203 lbp_r = strtoul(optarg, NULL, 0); 1204 if ((lbp_r != 0) && (lbp_r != 1)) 1205 errx(1, "valid values for -r are 0 and 1"); 1206 break; 1207 case 'v': 1208 verbose = 1; 1209 break; 1210 case 'w': 1211 lbp_w_set = 1; 1212 lbp_w = strtoul(optarg, NULL, 0); 1213 if ((lbp_w != 0) && (lbp_r != 1)) 1214 errx(1, "valid values for -r are 0 and 1"); 1215 break; 1216 default: 1217 break; 1218 } 1219 } 1220 1221 if ((rbdp_set + do_disable + do_enable + do_list + pi_length_set + 1222 prot_method_set + lbp_r_set + lbp_w_set) == 0) 1223 errx(1, "Need an argument for protect"); 1224 1225 if ((do_disable + do_enable + do_list) != 1) 1226 errx(1, "You must specify only one of -e, -d or -l"); 1227 1228 if (do_list != 0) { 1229 retval = mt_protect_print(status_data, verbose); 1230 goto bailout; 1231 } 1232 if (do_enable != 0) { 1233 /* 1234 * Enable protection, but allow the user to override 1235 * settings if he doesn't want everything turned on. 1236 */ 1237 if (rbdp_set == 0) 1238 rbdp = 1; 1239 if (lbp_w_set == 0) 1240 lbp_w = 1; 1241 if (lbp_r_set == 0) 1242 lbp_r = 1; 1243 /* 1244 * If the user doesn't override it, we default to enabling 1245 * Reed-Solomon checkums. 1246 */ 1247 if (prot_method_set == 0) 1248 prot_method = SA_CTRL_DP_REED_SOLOMON; 1249 if (pi_length_set == 0) 1250 pi_length = SA_CTRL_DP_RS_LENGTH; 1251 } else if (do_disable != 0) { 1252 /* 1253 * If the user wants to disable protection, we ignore any 1254 * other parameters he has set. Everything gets set to 0. 1255 */ 1256 rbdp = lbp_w = lbp_r = 0; 1257 prot_method = pi_length = 0; 1258 } 1259 1260 prot_entry = mt_status_entry_find(status_data, 1261 __DECONST(char *, MT_PROT_NAME)); 1262 if (prot_entry == NULL) 1263 errx(1, "Unable to find protection information status"); 1264 1265 supported_entry = mt_entry_find(prot_entry, 1266 __DECONST(char *, "protection_supported")); 1267 if (supported_entry == NULL) 1268 errx(1, "Unable to find protection support information"); 1269 1270 if (((supported_entry->var_type == MT_TYPE_INT) 1271 && (supported_entry->value_signed == 0)) 1272 || ((supported_entry->var_type == MT_TYPE_UINT) 1273 && (supported_entry->value_unsigned == 0))) 1274 errx(1, "This device does not support protection information"); 1275 1276 mt_protect_list[MT_PP_LBP_R].value = lbp_r; 1277 mt_protect_list[MT_PP_LBP_W].value = lbp_w; 1278 mt_protect_list[MT_PP_RBDP].value = rbdp; 1279 mt_protect_list[MT_PP_PI_LENGTH].value = pi_length; 1280 mt_protect_list[MT_PP_PROT_METHOD].value = prot_method; 1281 1282 bzero(¶ms, sizeof(params)); 1283 bzero(¶m_list, sizeof(param_list)); 1284 1285 /* 1286 * Go through the list and make sure that we have this parameter, 1287 * and that it is still an unsigned integer. If not, we've got a 1288 * problem. 1289 */ 1290 for (i = 0; i < MT_NUM_PROTECT_PARAMS; i++) { 1291 entry = mt_entry_find(prot_entry, 1292 __DECONST(char *, mt_protect_list[i].name)); 1293 if (entry == NULL) { 1294 errx(1, "Unable to find parameter %s", 1295 mt_protect_list[i].name); 1296 } 1297 mt_protect_list[i].entry = entry; 1298 1299 if (entry->var_type != MT_TYPE_UINT) 1300 errx(1, "Parameter %s is type %d, not unsigned, " 1301 "cannot proceed", mt_protect_list[i].name, 1302 entry->var_type); 1303 snprintf(params[i].value_name, sizeof(params[i].value_name), 1304 "%s.%s", MT_PROT_NAME, mt_protect_list[i].name); 1305 /* XXX KDM unify types here */ 1306 params[i].value_type = MT_PARAM_SET_UNSIGNED; 1307 params[i].value_len = sizeof(mt_protect_list[i].value); 1308 params[i].value.value_unsigned = mt_protect_list[i].value; 1309 1310 } 1311 param_list.num_params = MT_NUM_PROTECT_PARAMS; 1312 param_list.param_len = sizeof(params); 1313 param_list.params = params; 1314 1315 if (ioctl(mtfd, MTIOCSETLIST, ¶m_list) == -1) 1316 err(1, "error issuing MTIOCSETLIST ioctl"); 1317 1318 for (i = 0; i < MT_NUM_PROTECT_PARAMS; i++) { 1319 if (params[i].status != MT_PARAM_STATUS_OK) { 1320 warnx("%s", params[i].error_str); 1321 retval = 1; 1322 } 1323 } 1324 bailout: 1325 1326 return (retval); 1327 } 1328 1329 static int 1330 mt_param(int argc, char **argv, int mtfd, char *xml_str, 1331 struct mt_status_data *status_data) 1332 { 1333 int list = 0, do_set = 0, xml_dump = 0; 1334 char *param_name = NULL, *param_value = NULL; 1335 int retval = 0, quiet = 0; 1336 int c; 1337 1338 while ((c = getopt(argc, argv, "lp:qs:x")) != -1) { 1339 switch (c) { 1340 case 'l': 1341 list = 1; 1342 break; 1343 case 'p': 1344 if (param_name != NULL) { 1345 warnx("Only one paramter name may be " 1346 "specified"); 1347 retval = 1; 1348 goto bailout; 1349 } 1350 param_name = strdup(optarg); 1351 break; 1352 case 'q': 1353 quiet = 1; 1354 break; 1355 case 's': 1356 if (param_value != NULL) { 1357 warnx("Only one paramter value may be " 1358 "specified"); 1359 retval = 1; 1360 goto bailout; 1361 } 1362 param_value = strdup(optarg); 1363 do_set = 1; 1364 break; 1365 case 'x': 1366 xml_dump = 1; 1367 break; 1368 default: 1369 break; 1370 } 1371 } 1372 1373 if ((list + do_set + xml_dump) != 1) { 1374 warnx("You must specify only one of -s, -l or -x"); 1375 retval = 1; 1376 goto bailout; 1377 } 1378 1379 if (xml_dump != 0) { 1380 printf("%s", xml_str); 1381 retval = 0; 1382 goto bailout; 1383 } 1384 1385 if (do_set != 0) { 1386 if (param_name == NULL) 1387 errx(1, "You must specify -p with -s"); 1388 1389 retval = mt_set_param(mtfd, status_data, param_name, 1390 param_value); 1391 } else if (list != 0) 1392 retval = mt_param_list(status_data, param_name, quiet); 1393 1394 bailout: 1395 free(param_name); 1396 free(param_value); 1397 return (retval); 1398 } 1399 1400 int 1401 mt_print_density_entry(struct mt_status_entry *density_root, int indent) 1402 { 1403 struct mt_status_entry *entry; 1404 int retval = 0; 1405 1406 STAILQ_FOREACH(entry, &density_root->child_entries, links) { 1407 if (entry->var_type == MT_TYPE_NODE) { 1408 retval = mt_print_density_entry(entry, indent + 2); 1409 if (retval != 0) 1410 break; 1411 else 1412 continue; 1413 } 1414 if ((strcmp(entry->entry_name, "primary_density_code") == 0) 1415 || (strcmp(entry->entry_name, "secondary_density_code") == 0) 1416 || (strcmp(entry->entry_name, "density_code") == 0)) { 1417 1418 printf("%*s%s (%s): %s\n", indent, "", entry->desc ? 1419 entry->desc : "", entry->entry_name, 1420 denstostring(entry->value_unsigned)); 1421 } else if (strcmp(entry->entry_name, "density_flags") == 0) { 1422 printf("%*sMedium Access: ", indent, ""); 1423 if (entry->value_unsigned & MT_DENS_WRITE_OK) { 1424 printf("Read and Write\n"); 1425 } else { 1426 printf("Read Only\n"); 1427 } 1428 printf("%*sDefault Density: %s\n", indent, "", 1429 (entry->value_unsigned & MT_DENS_DEFLT) ? "Yes" : 1430 "No"); 1431 printf("%*sDuplicate Density: %s\n", indent, "", 1432 (entry->value_unsigned & MT_DENS_DUP) ? "Yes" : 1433 "No"); 1434 } else if (strcmp(entry->entry_name, "media_width") == 0) { 1435 printf("%*s%s (%s): %.1f mm\n", indent, "", 1436 entry->desc ? entry->desc : "", entry->entry_name, 1437 (double)((double)entry->value_unsigned / 10)); 1438 } else if (strcmp(entry->entry_name, "medium_length") == 0) { 1439 printf("%*s%s (%s): %ju m\n", indent, "", 1440 entry->desc ? entry->desc : "", entry->entry_name, 1441 (uintmax_t)entry->value_unsigned); 1442 } else if (strcmp(entry->entry_name, "capacity") == 0) { 1443 printf("%*s%s (%s): %ju MB\n", indent, "", entry->desc ? 1444 entry->desc : "", entry->entry_name, 1445 (uintmax_t)entry->value_unsigned); 1446 } else { 1447 printf("%*s%s (%s): %s\n", indent, "", entry->desc ? 1448 entry->desc : "", entry->entry_name, entry->value); 1449 } 1450 } 1451 1452 return (retval); 1453 } 1454 1455 int 1456 mt_print_density_report(struct mt_status_entry *report_root, int indent) 1457 { 1458 struct mt_status_entry *mt_report, *media_report; 1459 struct mt_status_entry *entry; 1460 int retval = 0; 1461 1462 mt_report = mt_entry_find(report_root, 1463 __DECONST(char *, MT_MEDIUM_TYPE_REPORT_NAME)); 1464 if (mt_report == NULL) 1465 return (1); 1466 1467 media_report = mt_entry_find(report_root, 1468 __DECONST(char *, MT_MEDIA_REPORT_NAME)); 1469 if (media_report == NULL) 1470 return (1); 1471 1472 if ((mt_report->value_signed == 0) 1473 && (media_report->value_signed == 0)) { 1474 printf("%*sThis tape drive supports the following " 1475 "media densities:\n", indent, ""); 1476 } else if ((mt_report->value_signed == 0) 1477 && (media_report->value_signed != 0)) { 1478 printf("%*sThe tape currently in this drive supports " 1479 "the following media densities:\n", indent, ""); 1480 } else if ((mt_report->value_signed != 0) 1481 && (media_report->value_signed == 0)) { 1482 printf("%*sThis tape drive supports the following " 1483 "media types:\n", indent, ""); 1484 } else { 1485 printf("%*sThis tape currently in this drive supports " 1486 "the following media types:\n", indent, ""); 1487 } 1488 1489 STAILQ_FOREACH(entry, &report_root->child_entries, links) { 1490 struct mt_status_nv *nv; 1491 1492 if (strcmp(entry->entry_name, MT_DENSITY_ENTRY_NAME) != 0) 1493 continue; 1494 1495 STAILQ_FOREACH(nv, &entry->nv_list, links) { 1496 if (strcmp(nv->name, "num") != 0) 1497 continue; 1498 1499 break; 1500 } 1501 1502 indent += 2; 1503 1504 printf("%*sDensity Entry", indent, ""); 1505 if (nv != NULL) 1506 printf(" %s", nv->value); 1507 printf(":\n"); 1508 1509 retval = mt_print_density_entry(entry, indent + 2); 1510 1511 indent -= 2; 1512 } 1513 1514 return (retval); 1515 } 1516 1517 int 1518 mt_print_density(struct mt_status_entry *density_root, int indent) 1519 { 1520 struct mt_status_entry *entry; 1521 int retval = 0; 1522 1523 /* 1524 * We should have this entry for every tape drive. This particular 1525 * value is reported via the mode page block header, not the 1526 * SCSI REPORT DENSITY SUPPORT command. 1527 */ 1528 entry = mt_entry_find(density_root, 1529 __DECONST(char *, MT_MEDIA_DENSITY_NAME)); 1530 if (entry == NULL) 1531 errx(1, "Unable to find node %s", MT_MEDIA_DENSITY_NAME); 1532 1533 printf("%*sCurrent density: %s\n", indent, "", 1534 denstostring(entry->value_unsigned)); 1535 1536 /* 1537 * It isn't an error if we don't have any density reports. Tape 1538 * drives that don't support the REPORT DENSITY SUPPORT command 1539 * won't have any; they will only have the current density entry 1540 * above. 1541 */ 1542 STAILQ_FOREACH(entry, &density_root->child_entries, links) { 1543 if (strcmp(entry->entry_name, MT_DENSITY_REPORT_NAME) != 0) 1544 continue; 1545 1546 retval = mt_print_density_report(entry, indent); 1547 } 1548 1549 return (retval); 1550 } 1551 1552 int 1553 mt_getdensity(int argc, char **argv, char *xml_str, 1554 struct mt_status_data *status_data) 1555 { 1556 int retval = 0; 1557 int verbose = 0, xml_dump = 0; 1558 struct mt_status_entry *density_root = NULL; 1559 int c; 1560 1561 while ((c = getopt(argc, argv, "vx")) != -1) { 1562 switch (c) { 1563 case 'v': 1564 verbose = 1; 1565 break; 1566 case 'x': 1567 xml_dump = 1; 1568 break; 1569 } 1570 } 1571 1572 if (xml_dump != 0) { 1573 printf("%s", xml_str); 1574 return (0); 1575 } 1576 1577 density_root = mt_status_entry_find(status_data, 1578 __DECONST(char *, MT_DENSITY_ROOT_NAME)); 1579 if (density_root == NULL) 1580 errx(1, "Cannot find density root node %s", 1581 MT_DENSITY_ROOT_NAME); 1582 1583 retval = mt_print_density(density_root, 0); 1584 1585 return (retval); 1586 } 1587 1588 static void 1589 warn_eof(void) 1590 { 1591 fprintf(stderr, 1592 "The \"eof\" command has been disabled.\n" 1593 "Use \"weof\" if you really want to write end-of-file marks,\n" 1594 "or \"eom\" if you rather want to skip to the end of " 1595 "recorded medium.\n"); 1596 exit(1); 1597 } 1598