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