1 /* $NetBSD: chio.c,v 1.6 1998/01/04 23:53:58 thorpej Exp $ */ 2 /*- 3 * Copyright (c) 1996 Jason R. Thorpe <thorpej@and.com> 4 * 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. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgements: 16 * This product includes software developed by Jason R. Thorpe 17 * for And Communications, http://www.and.com/ 18 * 4. The name of the author may not be used to endorse or promote products 19 * derived from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 /* 34 * Additional Copyright (c) 1997, by Matthew Jacob, for NASA/Ames Research Ctr. 35 * Addidional Copyright (c) 2000, by C. Stephen Gunn, Waterspout Communications 36 */ 37 38 #if 0 39 #ifndef lint 40 static const char copyright[] = 41 "@(#) Copyright (c) 1996 Jason R. Thorpe. All rights reserved."; 42 #endif /* not lint */ 43 #endif 44 45 #include <sys/cdefs.h> 46 __FBSDID("$FreeBSD$"); 47 48 #include <sys/param.h> 49 #include <sys/chio.h> 50 #include <err.h> 51 #include <fcntl.h> 52 #include <stdio.h> 53 #include <stdint.h> 54 #include <stdlib.h> 55 #include <string.h> 56 #include <unistd.h> 57 #include <langinfo.h> 58 #include <locale.h> 59 60 #include "defs.h" 61 #include "pathnames.h" 62 63 static void usage(void); 64 static void cleanup(void); 65 static u_int16_t parse_element_type(char *); 66 static u_int16_t parse_element_unit(char *); 67 static const char * element_type_name(int et); 68 static int parse_special(char *); 69 static int is_special(char *); 70 static const char *bits_to_string(ces_status_flags, const char *); 71 72 static void find_element(char *, uint16_t *, uint16_t *); 73 static struct changer_element_status *get_element_status 74 (unsigned int, unsigned int, int); 75 76 static int do_move(const char *, int, char **); 77 static int do_exchange(const char *, int, char **); 78 static int do_position(const char *, int, char **); 79 static int do_params(const char *, int, char **); 80 static int do_getpicker(const char *, int, char **); 81 static int do_setpicker(const char *, int, char **); 82 static int do_status(const char *, int, char **); 83 static int do_ielem(const char *, int, char **); 84 static int do_return(const char *, int, char **); 85 static int do_voltag(const char *, int, char **); 86 static void print_designator(const char *, u_int8_t, u_int8_t); 87 88 #ifndef CHET_VT 89 #define CHET_VT 10 /* Completely Arbitrary */ 90 #endif 91 92 /* Valid changer element types. */ 93 static const struct element_type elements[] = { 94 { "drive", CHET_DT }, 95 { "picker", CHET_MT }, 96 { "portal", CHET_IE }, 97 { "slot", CHET_ST }, 98 { "voltag", CHET_VT }, /* Select tapes by barcode */ 99 { NULL, 0 }, 100 }; 101 102 /* Valid commands. */ 103 static const struct changer_command commands[] = { 104 { "exchange", do_exchange }, 105 { "getpicker", do_getpicker }, 106 { "ielem", do_ielem }, 107 { "move", do_move }, 108 { "params", do_params }, 109 { "position", do_position }, 110 { "setpicker", do_setpicker }, 111 { "status", do_status }, 112 { "return", do_return }, 113 { "voltag", do_voltag }, 114 { NULL, 0 }, 115 }; 116 117 /* Valid special words. */ 118 static const struct special_word specials[] = { 119 { "inv", SW_INVERT }, 120 { "inv1", SW_INVERT1 }, 121 { "inv2", SW_INVERT2 }, 122 { NULL, 0 }, 123 }; 124 125 static int changer_fd; 126 static const char *changer_name; 127 128 int 129 main(int argc, char **argv) 130 { 131 int ch, i; 132 133 while ((ch = getopt(argc, argv, "f:")) != -1) { 134 switch (ch) { 135 case 'f': 136 changer_name = optarg; 137 break; 138 139 default: 140 usage(); 141 } 142 } 143 argc -= optind; 144 argv += optind; 145 146 if (argc == 0) 147 usage(); 148 149 /* Get the default changer if not already specified. */ 150 if (changer_name == NULL) 151 if ((changer_name = getenv(CHANGER_ENV_VAR)) == NULL) 152 changer_name = _PATH_CH; 153 154 /* Open the changer device. */ 155 if ((changer_fd = open(changer_name, O_RDWR, 0600)) == -1) 156 err(1, "%s: open", changer_name); 157 158 /* Register cleanup function. */ 159 if (atexit(cleanup)) 160 err(1, "can't register cleanup function"); 161 162 /* Find the specified command. */ 163 for (i = 0; commands[i].cc_name != NULL; ++i) 164 if (strcmp(*argv, commands[i].cc_name) == 0) 165 break; 166 if (commands[i].cc_name == NULL) { 167 /* look for abbreviation */ 168 for (i = 0; commands[i].cc_name != NULL; ++i) 169 if (strncmp(*argv, commands[i].cc_name, 170 strlen(*argv)) == 0) 171 break; 172 } 173 174 if (commands[i].cc_name == NULL) 175 errx(1, "unknown command: %s", *argv); 176 177 exit ((*commands[i].cc_handler)(commands[i].cc_name, argc, argv)); 178 /* NOTREACHED */ 179 } 180 181 static int 182 do_move(const char *cname, int argc, char **argv) 183 { 184 struct changer_move cmd; 185 int val; 186 187 /* 188 * On a move command, we expect the following: 189 * 190 * <from ET> <from EU> <to ET> <to EU> [inv] 191 * 192 * where ET == element type and EU == element unit. 193 */ 194 195 ++argv; --argc; 196 197 if (argc < 4) { 198 warnx("%s: too few arguments", cname); 199 goto usage; 200 } else if (argc > 5) { 201 warnx("%s: too many arguments", cname); 202 goto usage; 203 } 204 (void) memset(&cmd, 0, sizeof(cmd)); 205 206 /* <from ET> */ 207 cmd.cm_fromtype = parse_element_type(*argv); 208 ++argv; --argc; 209 210 /* Check for voltag virtual type */ 211 if (CHET_VT == cmd.cm_fromtype) { 212 find_element(*argv, &cmd.cm_fromtype, &cmd.cm_fromunit); 213 } else { 214 /* <from EU> */ 215 cmd.cm_fromunit = parse_element_unit(*argv); 216 } 217 ++argv; --argc; 218 219 /* <to ET> */ 220 cmd.cm_totype = parse_element_type(*argv); 221 ++argv; --argc; 222 223 /* Check for voltag virtual type, and report error */ 224 if (CHET_VT == cmd.cm_totype) 225 errx(1,"%s: voltag only makes sense as an element source", 226 cname); 227 228 /* <to EU> */ 229 cmd.cm_tounit = parse_element_unit(*argv); 230 ++argv; --argc; 231 232 /* Deal with optional command modifier. */ 233 if (argc) { 234 val = parse_special(*argv); 235 switch (val) { 236 case SW_INVERT: 237 cmd.cm_flags |= CM_INVERT; 238 break; 239 240 default: 241 errx(1, "%s: inappropriate modifier `%s'", 242 cname, *argv); 243 /* NOTREACHED */ 244 } 245 } 246 247 /* Send command to changer. */ 248 if (ioctl(changer_fd, CHIOMOVE, &cmd)) 249 err(1, "%s: CHIOMOVE", changer_name); 250 251 return (0); 252 253 usage: 254 (void) fprintf(stderr, "usage: %s %s " 255 "<from ET> <from EU> <to ET> <to EU> [inv]\n", getprogname(), cname); 256 return (1); 257 } 258 259 static int 260 do_exchange(const char *cname, int argc, char **argv) 261 { 262 struct changer_exchange cmd; 263 int val; 264 265 /* 266 * On an exchange command, we expect the following: 267 * 268 * <src ET> <src EU> <dst1 ET> <dst1 EU> [<dst2 ET> <dst2 EU>] [inv1] [inv2] 269 * 270 * where ET == element type and EU == element unit. 271 */ 272 273 ++argv; --argc; 274 275 if (argc < 4) { 276 warnx("%s: too few arguments", cname); 277 goto usage; 278 } else if (argc > 8) { 279 warnx("%s: too many arguments", cname); 280 goto usage; 281 } 282 (void) memset(&cmd, 0, sizeof(cmd)); 283 284 /* <src ET> */ 285 cmd.ce_srctype = parse_element_type(*argv); 286 ++argv; --argc; 287 288 /* Check for voltag virtual type */ 289 if (CHET_VT == cmd.ce_srctype) { 290 find_element(*argv, &cmd.ce_srctype, &cmd.ce_srcunit); 291 } else { 292 /* <from EU> */ 293 cmd.ce_srcunit = parse_element_unit(*argv); 294 } 295 ++argv; --argc; 296 297 /* <dst1 ET> */ 298 cmd.ce_fdsttype = parse_element_type(*argv); 299 ++argv; --argc; 300 301 /* Check for voltag virtual type */ 302 if (CHET_VT == cmd.ce_fdsttype) { 303 find_element(*argv, &cmd.ce_fdsttype, &cmd.ce_fdstunit); 304 } else { 305 /* <from EU> */ 306 cmd.ce_fdstunit = parse_element_unit(*argv); 307 } 308 ++argv; --argc; 309 310 /* 311 * If the next token is a special word or there are no more 312 * arguments, then this is a case of simple exchange. 313 * dst2 == src. 314 */ 315 if ((argc == 0) || is_special(*argv)) { 316 cmd.ce_sdsttype = cmd.ce_srctype; 317 cmd.ce_sdstunit = cmd.ce_srcunit; 318 goto do_special; 319 } 320 321 /* <dst2 ET> */ 322 cmd.ce_sdsttype = parse_element_type(*argv); 323 ++argv; --argc; 324 325 if (CHET_VT == cmd.ce_sdsttype) 326 errx(1,"%s %s: voltag only makes sense as an element source", 327 cname, *argv); 328 329 /* <dst2 EU> */ 330 cmd.ce_sdstunit = parse_element_unit(*argv); 331 ++argv; --argc; 332 333 do_special: 334 /* Deal with optional command modifiers. */ 335 while (argc) { 336 val = parse_special(*argv); 337 ++argv; --argc; 338 switch (val) { 339 case SW_INVERT1: 340 cmd.ce_flags |= CE_INVERT1; 341 break; 342 343 case SW_INVERT2: 344 cmd.ce_flags |= CE_INVERT2; 345 break; 346 347 default: 348 errx(1, "%s: inappropriate modifier `%s'", 349 cname, *argv); 350 /* NOTREACHED */ 351 } 352 } 353 354 /* Send command to changer. */ 355 if (ioctl(changer_fd, CHIOEXCHANGE, &cmd)) 356 err(1, "%s: CHIOEXCHANGE", changer_name); 357 358 return (0); 359 360 usage: 361 (void) fprintf(stderr, 362 "usage: %s %s <src ET> <src EU> <dst1 ET> <dst1 EU>\n" 363 " [<dst2 ET> <dst2 EU>] [inv1] [inv2]\n", 364 getprogname(), cname); 365 return (1); 366 } 367 368 static int 369 do_position(const char *cname, int argc, char **argv) 370 { 371 struct changer_position cmd; 372 int val; 373 374 /* 375 * On a position command, we expect the following: 376 * 377 * <to ET> <to EU> [inv] 378 * 379 * where ET == element type and EU == element unit. 380 */ 381 382 ++argv; --argc; 383 384 if (argc < 2) { 385 warnx("%s: too few arguments", cname); 386 goto usage; 387 } else if (argc > 3) { 388 warnx("%s: too many arguments", cname); 389 goto usage; 390 } 391 (void) memset(&cmd, 0, sizeof(cmd)); 392 393 /* <to ET> */ 394 cmd.cp_type = parse_element_type(*argv); 395 ++argv; --argc; 396 397 /* <to EU> */ 398 cmd.cp_unit = parse_element_unit(*argv); 399 ++argv; --argc; 400 401 /* Deal with optional command modifier. */ 402 if (argc) { 403 val = parse_special(*argv); 404 switch (val) { 405 case SW_INVERT: 406 cmd.cp_flags |= CP_INVERT; 407 break; 408 409 default: 410 errx(1, "%s: inappropriate modifier `%s'", 411 cname, *argv); 412 /* NOTREACHED */ 413 } 414 } 415 416 /* Send command to changer. */ 417 if (ioctl(changer_fd, CHIOPOSITION, &cmd)) 418 err(1, "%s: CHIOPOSITION", changer_name); 419 420 return (0); 421 422 usage: 423 (void) fprintf(stderr, "usage: %s %s <to ET> <to EU> [inv]\n", 424 getprogname(), cname); 425 return (1); 426 } 427 428 /* ARGSUSED */ 429 static int 430 do_params(const char *cname, int argc, char **argv __unused) 431 { 432 struct changer_params data; 433 int picker; 434 435 /* No arguments to this command. */ 436 437 ++argv; --argc; 438 439 if (argc) { 440 warnx("%s: no arguments expected", cname); 441 goto usage; 442 } 443 444 /* Get params from changer and display them. */ 445 (void) memset(&data, 0, sizeof(data)); 446 if (ioctl(changer_fd, CHIOGPARAMS, &data)) 447 err(1, "%s: CHIOGPARAMS", changer_name); 448 449 (void) printf("%s: %d slot%s, %d drive%s, %d picker%s", 450 changer_name, 451 data.cp_nslots, (data.cp_nslots > 1) ? "s" : "", 452 data.cp_ndrives, (data.cp_ndrives > 1) ? "s" : "", 453 data.cp_npickers, (data.cp_npickers > 1) ? "s" : ""); 454 if (data.cp_nportals) 455 (void) printf(", %d portal%s", data.cp_nportals, 456 (data.cp_nportals > 1) ? "s" : ""); 457 458 /* Get current picker from changer and display it. */ 459 if (ioctl(changer_fd, CHIOGPICKER, &picker)) 460 err(1, "%s: CHIOGPICKER", changer_name); 461 462 (void) printf("\n%s: current picker: %d\n", changer_name, picker); 463 464 return (0); 465 466 usage: 467 (void) fprintf(stderr, "usage: %s %s\n", getprogname(), cname); 468 return (1); 469 } 470 471 /* ARGSUSED */ 472 static int 473 do_getpicker(const char *cname, int argc, char **argv __unused) 474 { 475 int picker; 476 477 /* No arguments to this command. */ 478 479 ++argv; --argc; 480 481 if (argc) { 482 warnx("%s: no arguments expected", cname); 483 goto usage; 484 } 485 486 /* Get current picker from changer and display it. */ 487 if (ioctl(changer_fd, CHIOGPICKER, &picker)) 488 err(1, "%s: CHIOGPICKER", changer_name); 489 490 (void) printf("%s: current picker: %d\n", changer_name, picker); 491 492 return (0); 493 494 usage: 495 (void) fprintf(stderr, "usage: %s %s\n", getprogname(), cname); 496 return (1); 497 } 498 499 static int 500 do_setpicker(const char *cname, int argc, char **argv) 501 { 502 int picker; 503 504 ++argv; --argc; 505 506 if (argc < 1) { 507 warnx("%s: too few arguments", cname); 508 goto usage; 509 } else if (argc > 1) { 510 warnx("%s: too many arguments", cname); 511 goto usage; 512 } 513 514 picker = parse_element_unit(*argv); 515 516 /* Set the changer picker. */ 517 if (ioctl(changer_fd, CHIOSPICKER, &picker)) 518 err(1, "%s: CHIOSPICKER", changer_name); 519 520 return (0); 521 522 usage: 523 (void) fprintf(stderr, "usage: %s %s <picker>\n", getprogname(), cname); 524 return (1); 525 } 526 527 static int 528 do_status(const char *cname, int argc, char **argv) 529 { 530 struct changer_params cp; 531 struct changer_element_status_request cesr; 532 int i; 533 u_int16_t base, count, chet, schet, echet; 534 const char *description; 535 int pvoltag = 0; 536 int avoltag = 0; 537 int sense = 0; 538 int scsi = 0; 539 int source = 0; 540 int intaddr = 0; 541 int c; 542 543 count = 0; 544 base = 0; 545 description = NULL; 546 547 optind = optreset = 1; 548 while ((c = getopt(argc, argv, "vVsSbaI")) != -1) { 549 switch (c) { 550 case 'v': 551 pvoltag = 1; 552 break; 553 case 'V': 554 avoltag = 1; 555 break; 556 case 's': 557 sense = 1; 558 break; 559 case 'S': 560 source = 1; 561 break; 562 case 'b': 563 scsi = 1; 564 break; 565 case 'I': 566 intaddr = 1; 567 break; 568 case 'a': 569 pvoltag = avoltag = source = sense = scsi = intaddr = 1; 570 break; 571 default: 572 warnx("%s: bad option", cname); 573 goto usage; 574 } 575 } 576 577 argc -= optind; 578 argv += optind; 579 580 /* 581 * On a status command, we expect the following: 582 * 583 * [<ET> [<start> [<end>] ] ] 584 * 585 * where ET == element type, start == first element to report, 586 * end == number of elements to report 587 * 588 * If we get no arguments, we get the status of all 589 * known element types. 590 */ 591 if (argc > 3) { 592 warnx("%s: too many arguments", cname); 593 goto usage; 594 } 595 596 /* 597 * Get params from changer. Specifically, we need the element 598 * counts. 599 */ 600 if (ioctl(changer_fd, CHIOGPARAMS, (char *)&cp)) 601 err(1, "%s: CHIOGPARAMS", changer_name); 602 603 if (argc > 0) 604 schet = echet = parse_element_type(argv[0]); 605 else { 606 schet = CHET_MT; 607 echet = CHET_DT; 608 } 609 if (argc > 1) { 610 base = (u_int16_t)atol(argv[1]); 611 count = 1; 612 } 613 if (argc > 2) 614 count = (u_int16_t)atol(argv[2]) - base + 1; 615 616 for (chet = schet; chet <= echet; ++chet) { 617 switch (chet) { 618 case CHET_MT: 619 if (count == 0) 620 count = cp.cp_npickers; 621 else if (count > cp.cp_npickers) 622 errx(1, "not that many pickers in device"); 623 description = "picker"; 624 break; 625 626 case CHET_ST: 627 if (count == 0) 628 count = cp.cp_nslots; 629 else if (count > cp.cp_nslots) 630 errx(1, "not that many slots in device"); 631 description = "slot"; 632 break; 633 634 case CHET_IE: 635 if (count == 0) 636 count = cp.cp_nportals; 637 else if (count > cp.cp_nportals) 638 errx(1, "not that many portals in device"); 639 description = "portal"; 640 break; 641 642 case CHET_DT: 643 if (count == 0) 644 count = cp.cp_ndrives; 645 else if (count > cp.cp_ndrives) 646 errx(1, "not that many drives in device"); 647 description = "drive"; 648 break; 649 650 default: 651 /* To appease gcc -Wuninitialized. */ 652 count = 0; 653 description = NULL; 654 } 655 656 if (count == 0) { 657 if (argc == 0) 658 continue; 659 else { 660 printf("%s: no %s elements\n", 661 changer_name, description); 662 return (0); 663 } 664 } 665 666 bzero(&cesr, sizeof(cesr)); 667 cesr.cesr_element_type = chet; 668 cesr.cesr_element_base = base; 669 cesr.cesr_element_count = count; 670 /* Allocate storage for the status structures. */ 671 cesr.cesr_element_status = 672 (struct changer_element_status *) 673 calloc((size_t)count, sizeof(struct changer_element_status)); 674 675 if (!cesr.cesr_element_status) 676 errx(1, "can't allocate status storage"); 677 678 if (avoltag || pvoltag) 679 cesr.cesr_flags |= CESR_VOLTAGS; 680 681 if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr)) { 682 free(cesr.cesr_element_status); 683 err(1, "%s: CHIOGSTATUS", changer_name); 684 } 685 686 /* Dump the status for each reported element. */ 687 for (i = 0; i < count; ++i) { 688 struct changer_element_status *ces = 689 &(cesr.cesr_element_status[i]); 690 printf("%s %d: %s", description, ces->ces_addr, 691 bits_to_string(ces->ces_flags, 692 CESTATUS_BITS)); 693 if (sense) 694 printf(" sense: <0x%02x/0x%02x>", 695 ces->ces_sensecode, 696 ces->ces_sensequal); 697 if (pvoltag) 698 printf(" voltag: <%s:%d>", 699 ces->ces_pvoltag.cv_volid, 700 ces->ces_pvoltag.cv_serial); 701 if (avoltag) 702 printf(" avoltag: <%s:%d>", 703 ces->ces_avoltag.cv_volid, 704 ces->ces_avoltag.cv_serial); 705 if (source) { 706 if (ces->ces_flags & CES_SOURCE_VALID) 707 printf(" source: <%s %d>", 708 element_type_name( 709 ces->ces_source_type), 710 ces->ces_source_addr); 711 else 712 printf(" source: <>"); 713 } 714 if (intaddr) 715 printf(" intaddr: <%d>", ces->ces_int_addr); 716 if (scsi) { 717 printf(" scsi: <"); 718 if (ces->ces_flags & CES_SCSIID_VALID) 719 printf("%d", ces->ces_scsi_id); 720 else 721 putchar('?'); 722 putchar(':'); 723 if (ces->ces_flags & CES_LUN_VALID) 724 printf("%d", ces->ces_scsi_lun); 725 else 726 putchar('?'); 727 putchar('>'); 728 } 729 if (ces->ces_designator_length > 0) 730 print_designator(ces->ces_designator, 731 ces->ces_code_set, 732 ces->ces_designator_length); 733 putchar('\n'); 734 } 735 736 free(cesr.cesr_element_status); 737 count = 0; 738 } 739 740 return (0); 741 742 usage: 743 (void) fprintf(stderr, "usage: %s %s [-vVsSbaA] [<element type> [<start-addr> [<end-addr>] ] ]\n", 744 getprogname(), cname); 745 return (1); 746 } 747 748 static int 749 do_ielem(const char *cname, int argc, char **argv) 750 { 751 int timeout = 0; 752 753 if (argc == 2) { 754 timeout = atol(argv[1]); 755 } else if (argc > 1) { 756 warnx("%s: too many arguments", cname); 757 goto usage; 758 } 759 760 if (ioctl(changer_fd, CHIOIELEM, &timeout)) 761 err(1, "%s: CHIOIELEM", changer_name); 762 763 return (0); 764 765 usage: 766 (void) fprintf(stderr, "usage: %s %s [<timeout>]\n", 767 getprogname(), cname); 768 return (1); 769 } 770 771 static int 772 do_voltag(const char *cname, int argc, char **argv) 773 { 774 int force = 0; 775 int clear = 0; 776 int alternate = 0; 777 int c; 778 struct changer_set_voltag_request csvr; 779 780 bzero(&csvr, sizeof(csvr)); 781 782 optind = optreset = 1; 783 while ((c = getopt(argc, argv, "fca")) != -1) { 784 switch (c) { 785 case 'f': 786 force = 1; 787 break; 788 case 'c': 789 clear = 1; 790 break; 791 case 'a': 792 alternate = 1; 793 break; 794 default: 795 warnx("%s: bad option", cname); 796 goto usage; 797 } 798 } 799 800 argc -= optind; 801 argv += optind; 802 803 if (argc < 2) { 804 warnx("%s: missing element specification", cname); 805 goto usage; 806 } 807 808 csvr.csvr_type = parse_element_type(argv[0]); 809 csvr.csvr_addr = (u_int16_t)atol(argv[1]); 810 811 if (!clear) { 812 if (argc < 3 || argc > 4) { 813 warnx("%s: missing argument", cname); 814 goto usage; 815 } 816 817 if (force) 818 csvr.csvr_flags = CSVR_MODE_REPLACE; 819 else 820 csvr.csvr_flags = CSVR_MODE_SET; 821 822 if (strlen(argv[2]) > sizeof(csvr.csvr_voltag.cv_volid)) { 823 warnx("%s: volume label too long", cname); 824 goto usage; 825 } 826 827 strlcpy((char *)csvr.csvr_voltag.cv_volid, argv[2], 828 sizeof(csvr.csvr_voltag.cv_volid)); 829 830 if (argc == 4) { 831 csvr.csvr_voltag.cv_serial = (u_int16_t)atol(argv[3]); 832 } 833 } else { 834 if (argc != 2) { 835 warnx("%s: unexpected argument", cname); 836 goto usage; 837 } 838 csvr.csvr_flags = CSVR_MODE_CLEAR; 839 } 840 841 if (alternate) { 842 csvr.csvr_flags |= CSVR_ALTERNATE; 843 } 844 845 if (ioctl(changer_fd, CHIOSETVOLTAG, &csvr)) 846 err(1, "%s: CHIOSETVOLTAG", changer_name); 847 848 return 0; 849 usage: 850 (void) fprintf(stderr, 851 "usage: %s %s [-fca] <element> [<voltag> [<vsn>] ]\n", 852 getprogname(), cname); 853 return 1; 854 } 855 856 static u_int16_t 857 parse_element_type(char *cp) 858 { 859 int i; 860 861 for (i = 0; elements[i].et_name != NULL; ++i) 862 if (strcmp(elements[i].et_name, cp) == 0) 863 return ((u_int16_t)elements[i].et_type); 864 865 errx(1, "invalid element type `%s'", cp); 866 /* NOTREACHED */ 867 } 868 869 static const char * 870 element_type_name(int et) 871 { 872 int i; 873 874 for (i = 0; elements[i].et_name != NULL; i++) 875 if (elements[i].et_type == et) 876 return elements[i].et_name; 877 878 return "unknown"; 879 } 880 881 static u_int16_t 882 parse_element_unit(char *cp) 883 { 884 int i; 885 char *p; 886 887 i = (int)strtol(cp, &p, 10); 888 if ((i < 0) || (*p != '\0')) 889 errx(1, "invalid unit number `%s'", cp); 890 891 return ((u_int16_t)i); 892 } 893 894 static int 895 parse_special(char *cp) 896 { 897 int val; 898 899 val = is_special(cp); 900 if (val) 901 return (val); 902 903 errx(1, "invalid modifier `%s'", cp); 904 /* NOTREACHED */ 905 } 906 907 static int 908 is_special(char *cp) 909 { 910 int i; 911 912 for (i = 0; specials[i].sw_name != NULL; ++i) 913 if (strcmp(specials[i].sw_name, cp) == 0) 914 return (specials[i].sw_value); 915 916 return (0); 917 } 918 919 static const char * 920 bits_to_string(ces_status_flags v, const char *cp) 921 { 922 const char *np; 923 char f, sep, *bp; 924 static char buf[128]; 925 926 bp = buf; 927 (void) memset(buf, 0, sizeof(buf)); 928 929 for (sep = '<'; (f = *cp++) != 0; cp = np) { 930 for (np = cp; *np >= ' ';) 931 np++; 932 if (((int)v & (1 << (f - 1))) == 0) 933 continue; 934 (void) snprintf(bp, sizeof(buf) - (size_t)(bp - &buf[0]), 935 "%c%.*s", sep, (int)(long)(np - cp), cp); 936 bp += strlen(bp); 937 sep = ','; 938 } 939 if (sep != '<') 940 *bp = '>'; 941 942 return (buf); 943 } 944 /* 945 * do_return() 946 * 947 * Given an element reference, ask the changer/picker to move that 948 * element back to its source slot. 949 */ 950 static int 951 do_return(const char *cname, int argc, char **argv) 952 { 953 struct changer_element_status *ces; 954 struct changer_move cmd; 955 uint16_t type, element; 956 957 ++argv; --argc; 958 959 if (argc < 2) { 960 warnx("%s: too few arguments", cname); 961 goto usage; 962 } else if (argc > 3) { 963 warnx("%s: too many arguments", cname); 964 goto usage; 965 } 966 967 type = parse_element_type(*argv); 968 ++argv; --argc; 969 970 /* Handle voltag virtual Changer Element Type */ 971 if (CHET_VT == type) { 972 find_element(*argv, &type, &element); 973 } else { 974 element = parse_element_unit(*argv); 975 } 976 ++argv; --argc; 977 978 /* Get the status */ 979 ces = get_element_status((unsigned int)type, (unsigned int)element, 980 CHET_VT == type); 981 982 if (NULL == ces) 983 errx(1, "%s: null element status pointer", cname); 984 985 if (!(ces->ces_flags & CES_SOURCE_VALID)) 986 errx(1, "%s: no source information", cname); 987 988 (void) memset(&cmd, 0, sizeof(cmd)); 989 990 cmd.cm_fromtype = type; 991 cmd.cm_fromunit = element; 992 cmd.cm_totype = ces->ces_source_type; 993 cmd.cm_tounit = ces->ces_source_addr; 994 995 if (ioctl(changer_fd, CHIOMOVE, &cmd) == -1) 996 err(1, "%s: CHIOMOVE", changer_name); 997 free(ces); 998 999 return(0); 1000 1001 usage: 1002 (void) fprintf(stderr, "usage: %s %s " 1003 "<from ET> <from EU>\n", getprogname(), cname); 1004 return(1); 1005 } 1006 1007 /* 1008 * get_element_status() 1009 * 1010 * return a *cesr for the specified changer element. This 1011 * routing will malloc()/calloc() the memory. The caller 1012 * should free() it when done. 1013 */ 1014 static struct changer_element_status * 1015 get_element_status(unsigned int type, unsigned int element, int use_voltags) 1016 { 1017 struct changer_element_status_request cesr; 1018 struct changer_element_status *ces; 1019 1020 ces = (struct changer_element_status *) 1021 calloc((size_t)1, sizeof(struct changer_element_status)); 1022 1023 if (NULL == ces) 1024 errx(1, "can't allocate status storage"); 1025 1026 (void)memset(&cesr, 0, sizeof(cesr)); 1027 1028 cesr.cesr_element_type = (uint16_t)type; 1029 cesr.cesr_element_base = (uint16_t)element; 1030 cesr.cesr_element_count = 1; /* Only this one element */ 1031 if (use_voltags) 1032 cesr.cesr_flags |= CESR_VOLTAGS; /* Grab voltags as well */ 1033 cesr.cesr_element_status = ces; 1034 1035 if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) { 1036 free(ces); 1037 err(1, "%s: CHIOGSTATUS", changer_name); 1038 /* NOTREACHED */ 1039 } 1040 1041 return ces; 1042 } 1043 1044 1045 /* 1046 * find_element() 1047 * 1048 * Given a <voltag> find the chager element and unit, or exit 1049 * with an error if it isn't found. We grab the changer status 1050 * and iterate until we find a match, or crap out. 1051 */ 1052 static void 1053 find_element(char *voltag, uint16_t *et, uint16_t *eu) 1054 { 1055 struct changer_params cp; 1056 struct changer_element_status_request cesr; 1057 struct changer_element_status *ch_ces, *ces; 1058 int found = 0; 1059 size_t elem, total_elem; 1060 1061 /* 1062 * Get the changer parameters, we're interested in the counts 1063 * for all types of elements to perform our search. 1064 */ 1065 if (ioctl(changer_fd, CHIOGPARAMS, (char *)&cp)) 1066 err(1, "%s: CHIOGPARAMS", changer_name); 1067 1068 /* Allocate some memory for the results */ 1069 total_elem = (cp.cp_nslots + cp.cp_ndrives 1070 + cp.cp_npickers + cp.cp_nportals); 1071 1072 ch_ces = (struct changer_element_status *) 1073 calloc(total_elem, sizeof(struct changer_element_status)); 1074 1075 if (NULL == ch_ces) 1076 errx(1, "can't allocate status storage"); 1077 1078 ces = ch_ces; 1079 1080 /* Read in the changer slots */ 1081 if (cp.cp_nslots > 0) { 1082 (void) memset(&cesr, 0, sizeof(cesr)); 1083 cesr.cesr_element_type = CHET_ST; 1084 cesr.cesr_element_base = 0; 1085 cesr.cesr_element_count = cp.cp_nslots; 1086 cesr.cesr_flags |= CESR_VOLTAGS; 1087 cesr.cesr_element_status = ces; 1088 1089 if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) { 1090 free(ch_ces); 1091 err(1, "%s: CHIOGSTATUS", changer_name); 1092 } 1093 ces += cp.cp_nslots; 1094 } 1095 1096 /* Read in the drive information */ 1097 if (cp.cp_ndrives > 0 ) { 1098 1099 (void) memset(&cesr, 0, sizeof(cesr)); 1100 cesr.cesr_element_type = CHET_DT; 1101 cesr.cesr_element_base = 0; 1102 cesr.cesr_element_count = cp.cp_ndrives; 1103 cesr.cesr_flags |= CESR_VOLTAGS; 1104 cesr.cesr_element_status = ces; 1105 1106 if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) { 1107 free(ch_ces); 1108 err(1, "%s: CHIOGSTATUS", changer_name); 1109 } 1110 ces += cp.cp_ndrives; 1111 } 1112 1113 /* Read in the portal information */ 1114 if (cp.cp_nportals > 0 ) { 1115 (void) memset(&cesr, 0, sizeof(cesr)); 1116 cesr.cesr_element_type = CHET_IE; 1117 cesr.cesr_element_base = 0; 1118 cesr.cesr_element_count = cp.cp_nportals; 1119 cesr.cesr_flags |= CESR_VOLTAGS; 1120 cesr.cesr_element_status = ces; 1121 1122 if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) { 1123 free(ch_ces); 1124 err(1, "%s: CHIOGSTATUS", changer_name); 1125 } 1126 ces += cp.cp_nportals; 1127 } 1128 1129 /* Read in the picker information */ 1130 if (cp.cp_npickers > 0) { 1131 (void) memset(&cesr, 0, sizeof(cesr)); 1132 cesr.cesr_element_type = CHET_MT; 1133 cesr.cesr_element_base = 0; 1134 cesr.cesr_element_count = cp.cp_npickers; 1135 cesr.cesr_flags |= CESR_VOLTAGS; 1136 cesr.cesr_element_status = ces; 1137 1138 if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) { 1139 free(ch_ces); 1140 err(1, "%s: CHIOGSTATUS", changer_name); 1141 } 1142 } 1143 1144 /* 1145 * Now search the list the specified <voltag> 1146 */ 1147 for (elem = 0; elem < total_elem; ++elem) { 1148 1149 ces = &ch_ces[elem]; 1150 1151 /* Make sure we have a tape in this element */ 1152 if ((ces->ces_flags & (CES_STATUS_ACCESS|CES_STATUS_FULL)) 1153 != (CES_STATUS_ACCESS|CES_STATUS_FULL)) 1154 continue; 1155 1156 /* Check to see if it is our target */ 1157 if (strcasecmp(voltag, 1158 (const char *)ces->ces_pvoltag.cv_volid) == 0) { 1159 *et = ces->ces_type; 1160 *eu = ces->ces_addr; 1161 ++found; 1162 break; 1163 } 1164 } 1165 if (!found) { 1166 errx(1, "%s: unable to locate voltag: %s", changer_name, 1167 voltag); 1168 } 1169 free(ch_ces); 1170 return; 1171 } 1172 1173 static void 1174 cleanup(void) 1175 { 1176 /* Simple enough... */ 1177 (void)close(changer_fd); 1178 } 1179 1180 static void 1181 usage(void) 1182 { 1183 (void)fprintf(stderr, "usage: %s [-f changer] command [-<flags>] " 1184 "arg1 arg2 [arg3 [...]]\n", getprogname()); 1185 exit(1); 1186 } 1187 1188 #define UTF8CODESET "UTF-8" 1189 1190 static void 1191 print_designator(const char *designator, u_int8_t code_set, 1192 u_int8_t designator_length) 1193 { 1194 printf(" serial number: <"); 1195 switch (code_set) { 1196 case CES_CODE_SET_ASCII: { 1197 /* 1198 * The driver insures that the string is always NUL terminated. 1199 */ 1200 printf("%s", designator); 1201 break; 1202 } 1203 case CES_CODE_SET_UTF_8: { 1204 char *cs_native; 1205 1206 setlocale(LC_ALL, ""); 1207 cs_native = nl_langinfo(CODESET); 1208 1209 /* See if we can natively print UTF-8 */ 1210 if (strcmp(cs_native, UTF8CODESET) == 0) 1211 cs_native = NULL; 1212 1213 if (cs_native == NULL) { 1214 /* We can natively print UTF-8, so use printf. */ 1215 printf("%s", designator); 1216 } else { 1217 int i; 1218 1219 /* 1220 * We can't natively print UTF-8. We should 1221 * convert it to the terminal's codeset, but that 1222 * requires iconv(3) and FreeBSD doesn't have 1223 * iconv(3) in the base system yet. So we use %XX 1224 * notation for non US-ASCII characters instead. 1225 */ 1226 for (i = 0; i < designator_length && 1227 designator[i] != '\0'; i++) { 1228 if ((unsigned char)designator[i] < 0x80) 1229 printf("%c", designator[i]); 1230 else 1231 printf("%%%02x", 1232 (unsigned char)designator[i]); 1233 } 1234 } 1235 break; 1236 } 1237 case CES_CODE_SET_BINARY: { 1238 int i; 1239 1240 for (i = 0; i < designator_length; i++) 1241 printf("%02X%s", designator[i], 1242 (i == designator_length - 1) ? "" : " "); 1243 break; 1244 } 1245 default: 1246 break; 1247 } 1248 printf(">"); 1249 } 1250