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