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