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