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