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 */ 36 37 #include <sys/cdefs.h> 38 #ifndef lint 39 __COPYRIGHT("@(#) Copyright (c) 1996 Jason R. Thorpe. All rights reserved."); 40 __RCSID("$NetBSD: chio.c,v 1.6 1998/01/04 23:53:58 thorpej Exp $"); 41 #endif 42 43 #include <sys/param.h> 44 #include <sys/chio.h> 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 extern int optreset; /* from getopt.o */ 57 58 int main(int, char *[]); 59 static void usage(void); 60 static void cleanup(void); 61 static int parse_element_type(char *); 62 static int parse_element_unit(char *); 63 static const char * element_type_name(int et); 64 static int parse_special(char *); 65 static int is_special(char *); 66 static char *bits_to_string(int, const char *); 67 68 static int do_move(char *, int, char **); 69 static int do_exchange(char *, int, char **); 70 static int do_position(char *, int, char **); 71 static int do_params(char *, int, char **); 72 static int do_getpicker(char *, int, char **); 73 static int do_setpicker(char *, int, char **); 74 static int do_status(char *, int, char **); 75 static int do_ielem(char *, int, char **); 76 static int do_voltag(char *, int, char **); 77 78 /* Valid changer element types. */ 79 const struct element_type elements[] = { 80 { "picker", CHET_MT }, 81 { "slot", CHET_ST }, 82 { "portal", CHET_IE }, 83 { "drive", CHET_DT }, 84 { NULL, 0 }, 85 }; 86 87 /* Valid commands. */ 88 const struct changer_command commands[] = { 89 { "move", do_move }, 90 { "exchange", do_exchange }, 91 { "position", do_position }, 92 { "params", do_params }, 93 { "getpicker", do_getpicker }, 94 { "setpicker", do_setpicker }, 95 { "status", do_status }, 96 { "ielem", do_ielem }, 97 { "voltag", do_voltag }, 98 { NULL, 0 }, 99 }; 100 101 /* Valid special words. */ 102 const struct special_word specials[] = { 103 { "inv", SW_INVERT }, 104 { "inv1", SW_INVERT1 }, 105 { "inv2", SW_INVERT2 }, 106 { NULL, 0 }, 107 }; 108 109 static int changer_fd; 110 static char *changer_name; 111 112 int 113 main(int argc, char *argv[]) 114 { 115 int ch, i; 116 117 while ((ch = getopt(argc, argv, "f:")) != -1) { 118 switch (ch) { 119 case 'f': 120 changer_name = optarg; 121 break; 122 123 default: 124 usage(); 125 } 126 } 127 argc -= optind; 128 argv += optind; 129 130 if (argc == 0) 131 usage(); 132 133 /* Get the default changer if not already specified. */ 134 if (changer_name == NULL) 135 if ((changer_name = getenv(CHANGER_ENV_VAR)) == NULL) 136 changer_name = _PATH_CH; 137 138 /* Open the changer device. */ 139 if ((changer_fd = open(changer_name, O_RDWR, 0600)) == -1) 140 err(1, "%s: open", changer_name); 141 142 /* Register cleanup function. */ 143 if (atexit(cleanup)) 144 err(1, "can't register cleanup function"); 145 146 /* Find the specified command. */ 147 for (i = 0; commands[i].cc_name != NULL; ++i) 148 if (strcmp(*argv, commands[i].cc_name) == 0) 149 break; 150 if (commands[i].cc_name == NULL) 151 errx(1, "unknown command: %s", *argv); 152 153 exit((*commands[i].cc_handler)(commands[i].cc_name, argc, argv)); 154 /* NOTREACHED */ 155 } 156 157 static int 158 do_move(char *cname, int argc, char **argv) 159 { 160 struct changer_move cmd; 161 int val; 162 163 /* 164 * On a move command, we expect the following: 165 * 166 * <from ET> <from EU> <to ET> <to EU> [inv] 167 * 168 * where ET == element type and EU == element unit. 169 */ 170 171 ++argv; --argc; 172 173 if (argc < 4) { 174 warnx("%s: too few arguments", cname); 175 goto usage; 176 } else if (argc > 5) { 177 warnx("%s: too many arguments", cname); 178 goto usage; 179 } 180 (void) memset(&cmd, 0, sizeof(cmd)); 181 182 /* <from ET> */ 183 cmd.cm_fromtype = parse_element_type(*argv); 184 ++argv; --argc; 185 186 /* <from EU> */ 187 cmd.cm_fromunit = parse_element_unit(*argv); 188 ++argv; --argc; 189 190 /* <to ET> */ 191 cmd.cm_totype = parse_element_type(*argv); 192 ++argv; --argc; 193 194 /* <to EU> */ 195 cmd.cm_tounit = parse_element_unit(*argv); 196 ++argv; --argc; 197 198 /* Deal with optional command modifier. */ 199 if (argc) { 200 val = parse_special(*argv); 201 switch (val) { 202 case SW_INVERT: 203 cmd.cm_flags |= CM_INVERT; 204 break; 205 206 default: 207 errx(1, "%s: inappropriate modifier `%s'", 208 cname, *argv); 209 /* NOTREACHED */ 210 } 211 } 212 213 /* Send command to changer. */ 214 if (ioctl(changer_fd, CHIOMOVE, &cmd)) 215 err(1, "%s: CHIOMOVE", changer_name); 216 217 return (0); 218 219 usage: 220 (void) fprintf(stderr, "usage: %s %s " 221 "<from ET> <from EU> <to ET> <to EU> [inv]\n", __progname, cname); 222 return (1); 223 } 224 225 static int 226 do_exchange(char *cname, int argc, char **argv) 227 { 228 struct changer_exchange cmd; 229 int val; 230 231 /* 232 * On an exchange command, we expect the following: 233 * 234 * <src ET> <src EU> <dst1 ET> <dst1 EU> [<dst2 ET> <dst2 EU>] [inv1] [inv2] 235 * 236 * where ET == element type and EU == element unit. 237 */ 238 239 ++argv; --argc; 240 241 if (argc < 4) { 242 warnx("%s: too few arguments", cname); 243 goto usage; 244 } else if (argc > 8) { 245 warnx("%s: too many arguments", cname); 246 goto usage; 247 } 248 (void) memset(&cmd, 0, sizeof(cmd)); 249 250 /* <src ET> */ 251 cmd.ce_srctype = parse_element_type(*argv); 252 ++argv; --argc; 253 254 /* <src EU> */ 255 cmd.ce_srcunit = parse_element_unit(*argv); 256 ++argv; --argc; 257 258 /* <dst1 ET> */ 259 cmd.ce_fdsttype = parse_element_type(*argv); 260 ++argv; --argc; 261 262 /* <dst1 EU> */ 263 cmd.ce_fdstunit = parse_element_unit(*argv); 264 ++argv; --argc; 265 266 /* 267 * If the next token is a special word or there are no more 268 * arguments, then this is a case of simple exchange. 269 * dst2 == src. 270 */ 271 if ((argc == 0) || is_special(*argv)) { 272 cmd.ce_sdsttype = cmd.ce_srctype; 273 cmd.ce_sdstunit = cmd.ce_srcunit; 274 goto do_special; 275 } 276 277 /* <dst2 ET> */ 278 cmd.ce_sdsttype = parse_element_type(*argv); 279 ++argv; --argc; 280 281 /* <dst2 EU> */ 282 cmd.ce_sdstunit = parse_element_unit(*argv); 283 ++argv; --argc; 284 285 do_special: 286 /* Deal with optional command modifiers. */ 287 while (argc) { 288 val = parse_special(*argv); 289 ++argv; --argc; 290 switch (val) { 291 case SW_INVERT1: 292 cmd.ce_flags |= CE_INVERT1; 293 break; 294 295 case SW_INVERT2: 296 cmd.ce_flags |= CE_INVERT2; 297 break; 298 299 default: 300 errx(1, "%s: inappropriate modifier `%s'", 301 cname, *argv); 302 /* NOTREACHED */ 303 } 304 } 305 306 /* Send command to changer. */ 307 if (ioctl(changer_fd, CHIOEXCHANGE, &cmd)) 308 err(1, "%s: CHIOEXCHANGE", changer_name); 309 310 return (0); 311 312 usage: 313 (void) fprintf(stderr, 314 "usage: %s %s <src ET> <src EU> <dst1 ET> <dst1 EU>\n" 315 " [<dst2 ET> <dst2 EU>] [inv1] [inv2]\n", 316 __progname, cname); 317 return (1); 318 } 319 320 static int 321 do_position(char *cname, int argc, char **argv) 322 { 323 struct changer_position cmd; 324 int val; 325 326 /* 327 * On a position command, we expect the following: 328 * 329 * <to ET> <to EU> [inv] 330 * 331 * where ET == element type and EU == element unit. 332 */ 333 334 ++argv; --argc; 335 336 if (argc < 2) { 337 warnx("%s: too few arguments", cname); 338 goto usage; 339 } else if (argc > 3) { 340 warnx("%s: too many arguments", cname); 341 goto usage; 342 } 343 (void) memset(&cmd, 0, sizeof(cmd)); 344 345 /* <to ET> */ 346 cmd.cp_type = parse_element_type(*argv); 347 ++argv; --argc; 348 349 /* <to EU> */ 350 cmd.cp_unit = parse_element_unit(*argv); 351 ++argv; --argc; 352 353 /* Deal with optional command modifier. */ 354 if (argc) { 355 val = parse_special(*argv); 356 switch (val) { 357 case SW_INVERT: 358 cmd.cp_flags |= CP_INVERT; 359 break; 360 361 default: 362 errx(1, "%s: inappropriate modifier `%s'", 363 cname, *argv); 364 /* NOTREACHED */ 365 } 366 } 367 368 /* Send command to changer. */ 369 if (ioctl(changer_fd, CHIOPOSITION, &cmd)) 370 err(1, "%s: CHIOPOSITION", changer_name); 371 372 return (0); 373 374 usage: 375 (void) fprintf(stderr, "usage: %s %s <to ET> <to EU> [inv]\n", 376 __progname, cname); 377 return (1); 378 } 379 380 /* ARGSUSED */ 381 static int 382 do_params(char *cname, int argc, char **argv) 383 { 384 struct changer_params data; 385 int picker; 386 387 /* No arguments to this command. */ 388 389 ++argv; --argc; 390 391 if (argc) { 392 warnx("%s: no arguements expected", cname); 393 goto usage; 394 } 395 396 /* Get params from changer and display them. */ 397 (void) memset(&data, 0, sizeof(data)); 398 if (ioctl(changer_fd, CHIOGPARAMS, &data)) 399 err(1, "%s: CHIOGPARAMS", changer_name); 400 401 (void) printf("%s: %d slot%s, %d drive%s, %d picker%s", 402 changer_name, 403 data.cp_nslots, (data.cp_nslots > 1) ? "s" : "", 404 data.cp_ndrives, (data.cp_ndrives > 1) ? "s" : "", 405 data.cp_npickers, (data.cp_npickers > 1) ? "s" : ""); 406 if (data.cp_nportals) 407 (void) printf(", %d portal%s", data.cp_nportals, 408 (data.cp_nportals > 1) ? "s" : ""); 409 410 /* Get current picker from changer and display it. */ 411 if (ioctl(changer_fd, CHIOGPICKER, &picker)) 412 err(1, "%s: CHIOGPICKER", changer_name); 413 414 (void) printf("\n%s: current picker: %d\n", changer_name, picker); 415 416 return (0); 417 418 usage: 419 (void) fprintf(stderr, "usage: %s %s\n", __progname, cname); 420 return (1); 421 } 422 423 /* ARGSUSED */ 424 static int 425 do_getpicker(char *cname, int argc, char **argv) 426 { 427 int picker; 428 429 /* No arguments to this command. */ 430 431 ++argv; --argc; 432 433 if (argc) { 434 warnx("%s: no arguments expected", cname); 435 goto usage; 436 } 437 438 /* Get current picker from changer and display it. */ 439 if (ioctl(changer_fd, CHIOGPICKER, &picker)) 440 err(1, "%s: CHIOGPICKER", changer_name); 441 442 (void) printf("%s: current picker: %d\n", changer_name, picker); 443 444 return (0); 445 446 usage: 447 (void) fprintf(stderr, "usage: %s %s\n", __progname, cname); 448 return (1); 449 } 450 451 static int 452 do_setpicker(char *cname, int argc, char **argv) 453 { 454 int picker; 455 456 ++argv; --argc; 457 458 if (argc < 1) { 459 warnx("%s: too few arguments", cname); 460 goto usage; 461 } else if (argc > 1) { 462 warnx("%s: too many arguments", cname); 463 goto usage; 464 } 465 466 picker = parse_element_unit(*argv); 467 468 /* Set the changer picker. */ 469 if (ioctl(changer_fd, CHIOSPICKER, &picker)) 470 err(1, "%s: CHIOSPICKER", changer_name); 471 472 return (0); 473 474 usage: 475 (void) fprintf(stderr, "usage: %s %s <picker>\n", __progname, cname); 476 return (1); 477 } 478 479 static int 480 do_status(char *cname, int argc, char **argv) 481 { 482 struct changer_params cp; 483 struct changer_element_status_request cesr; 484 int i, count, base, chet, schet, echet; 485 char *description; 486 int pvoltag = 0; 487 int avoltag = 0; 488 int sense = 0; 489 int scsi = 0; 490 int source = 0; 491 int intaddr = 0; 492 int c; 493 494 count = 0; 495 base = 0; 496 description = NULL; 497 498 optind = optreset = 1; 499 while ((c = getopt(argc, argv, "vVsSbaI")) != EOF) { 500 switch (c) { 501 case 'v': 502 pvoltag = 1; 503 break; 504 case 'V': 505 avoltag = 1; 506 break; 507 case 's': 508 sense = 1; 509 break; 510 case 'S': 511 source = 1; 512 break; 513 case 'b': 514 scsi = 1; 515 break; 516 case 'I': 517 intaddr = 1; 518 break; 519 case 'a': 520 pvoltag = avoltag = source = sense = scsi = intaddr = 1; 521 break; 522 default: 523 warnx("bad option", cname); 524 goto usage; 525 } 526 } 527 528 argc -= optind; 529 argv += optind; 530 531 /* 532 * On a status command, we expect the following: 533 * 534 * [<ET> [<start> [<end>] ] ] 535 * 536 * where ET == element type, start == first element to report, 537 * end == number of elements to report 538 * 539 * If we get no arguments, we get the status of all 540 * known element types. 541 */ 542 if (argc > 3) { 543 warnx("%s: too many arguments", cname); 544 goto usage; 545 } 546 547 /* 548 * Get params from changer. Specifically, we need the element 549 * counts. 550 */ 551 if (ioctl(changer_fd, CHIOGPARAMS, (char *)&cp)) 552 err(1, "%s: CHIOGPARAMS", changer_name); 553 554 if (argc > 0) 555 schet = echet = parse_element_type(argv[0]); 556 else { 557 schet = CHET_MT; 558 echet = CHET_DT; 559 } 560 if (argc > 1) { 561 base = atol(argv[1]); 562 count = 1; 563 } 564 if (argc > 2) 565 count = atol(argv[2]) - base + 1; 566 567 if (base < 0 || count < 0) 568 errx(1, "bad arguments"); 569 570 for (chet = schet; chet <= echet; ++chet) { 571 switch (chet) { 572 case CHET_MT: 573 if (count == 0) 574 count = cp.cp_npickers; 575 else if (count > cp.cp_npickers) 576 errx(1, "not that many pickers in device"); 577 description = "picker"; 578 break; 579 580 case CHET_ST: 581 if (count == 0) 582 count = cp.cp_nslots; 583 else if (count > cp.cp_nslots) 584 errx(1, "not that many slots in device"); 585 description = "slot"; 586 break; 587 588 case CHET_IE: 589 if (count == 0) 590 count = cp.cp_nportals; 591 else if (count > cp.cp_nportals) 592 errx(1, "not that many portals in device"); 593 description = "portal"; 594 break; 595 596 case CHET_DT: 597 if (count == 0) 598 count = cp.cp_ndrives; 599 else if (count > cp.cp_ndrives) 600 errx(1, "not that many drives in device"); 601 description = "drive"; 602 break; 603 604 default: 605 /* To appease gcc -Wuninitialized. */ 606 count = 0; 607 description = NULL; 608 } 609 610 if (count == 0) { 611 if (argc == 0) 612 continue; 613 else { 614 printf("%s: no %s elements\n", 615 changer_name, description); 616 return (0); 617 } 618 } 619 620 bzero(&cesr, sizeof(cesr)); 621 cesr.cesr_element_type = chet; 622 cesr.cesr_element_base = base; 623 cesr.cesr_element_count = count; 624 /* Allocate storage for the status structures. */ 625 cesr.cesr_element_status 626 = (struct changer_element_status *) 627 malloc(count * sizeof(struct changer_element_status)); 628 629 if (!cesr.cesr_element_status) 630 errx(1, "can't allocate status storage"); 631 632 if (avoltag || pvoltag) 633 cesr.cesr_flags |= CESR_VOLTAGS; 634 635 if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr)) { 636 free(cesr.cesr_element_status); 637 err(1, "%s: CHIOGSTATUS", changer_name); 638 } 639 640 /* Dump the status for each reported element. */ 641 for (i = 0; i < count; ++i) { 642 struct changer_element_status *ces = 643 &(cesr.cesr_element_status[i]); 644 printf("%s %d: %s", description, ces->ces_addr, 645 bits_to_string(ces->ces_flags, 646 CESTATUS_BITS)); 647 if (sense) 648 printf(" sense: <0x%02x/0x%02x>", 649 ces->ces_sensecode, 650 ces->ces_sensequal); 651 if (pvoltag) 652 printf(" voltag: <%s:%d>", 653 ces->ces_pvoltag.cv_volid, 654 ces->ces_pvoltag.cv_serial); 655 if (avoltag) 656 printf(" avoltag: <%s:%d>", 657 ces->ces_avoltag.cv_volid, 658 ces->ces_avoltag.cv_serial); 659 if (source) 660 if (ces->ces_flags & CES_SOURCE_VALID) 661 printf(" source: <%s %d>", 662 element_type_name( 663 ces->ces_source_type), 664 ces->ces_source_addr); 665 else 666 printf(" source: <>"); 667 if (intaddr) 668 printf(" intaddr: <%d>", ces->ces_int_addr); 669 if (scsi) { 670 printf(" scsi: <"); 671 if (ces->ces_flags & CES_SCSIID_VALID) 672 printf("%d", ces->ces_scsi_id); 673 else 674 putchar('?'); 675 putchar(':'); 676 if (ces->ces_flags & CES_LUN_VALID) 677 printf("%d", ces->ces_scsi_lun); 678 else 679 putchar('?'); 680 putchar('>'); 681 } 682 putchar('\n'); 683 } 684 685 free(cesr.cesr_element_status); 686 count = 0; 687 } 688 689 return (0); 690 691 usage: 692 (void) fprintf(stderr, "usage: %s %s [-vVsSbaA] [<element type> [<start-addr> [<end-addr>] ] ]\n", 693 __progname, cname); 694 return (1); 695 } 696 697 static int 698 do_ielem(char *cname, int argc, char **argv) 699 { 700 int timeout = 0; 701 702 if (argc == 2) { 703 timeout = atol(argv[1]); 704 } else if (argc > 1) { 705 warnx("%s: too many arguments", cname); 706 goto usage; 707 } 708 709 if (ioctl(changer_fd, CHIOIELEM, &timeout)) 710 err(1, "%s: CHIOIELEM", changer_name); 711 712 return (0); 713 714 usage: 715 (void) fprintf(stderr, "usage: %s %s [<timeout>]\n", 716 __progname, cname); 717 return (1); 718 } 719 720 static int 721 do_voltag(char *cname, int argc, char **argv) 722 { 723 int force = 0; 724 int clear = 0; 725 int alternate = 0; 726 int c; 727 struct changer_set_voltag_request csvr; 728 729 bzero(&csvr, sizeof(csvr)); 730 731 optind = optreset = 1; 732 while ((c = getopt(argc, argv, "fca")) != EOF) { 733 switch (c) { 734 case 'f': 735 force = 1; 736 break; 737 case 'c': 738 clear = 1; 739 break; 740 case 'a': 741 alternate = 1; 742 break; 743 default: 744 warnx("bad option", cname); 745 goto usage; 746 } 747 } 748 749 argc -= optind; 750 argv += optind; 751 752 if (argc < 2) { 753 warnx("missing element specification", cname); 754 goto usage; 755 } 756 757 csvr.csvr_type = parse_element_type(argv[0]); 758 csvr.csvr_addr = atol(argv[1]); 759 760 if (!clear) { 761 if (argc < 3 || argc > 4) { 762 warnx("missing argument", cname); 763 goto usage; 764 } 765 766 if (force) 767 csvr.csvr_flags = CSVR_MODE_REPLACE; 768 else 769 csvr.csvr_flags = CSVR_MODE_SET; 770 771 if (strlen(argv[2]) > sizeof(csvr.csvr_voltag.cv_volid)) { 772 warnx("volume label too long", cname); 773 goto usage; 774 } 775 776 strncpy(csvr.csvr_voltag.cv_volid, argv[2], 777 sizeof(csvr.csvr_voltag.cv_volid)); 778 779 if (argc == 4) { 780 csvr.csvr_voltag.cv_serial = atol(argv[3]); 781 } 782 } else { 783 if (argc != 2) { 784 warnx("unexpected argument", cname); 785 goto usage; 786 } 787 csvr.csvr_flags = CSVR_MODE_CLEAR; 788 } 789 790 if (alternate) { 791 csvr.csvr_flags |= CSVR_ALTERNATE; 792 } 793 794 if (ioctl(changer_fd, CHIOSETVOLTAG, &csvr)) 795 err(1, "%s: CHIOSETVOLTAG", changer_name); 796 797 return 0; 798 usage: 799 (void) fprintf(stderr, 800 "usage: %s %s [-fca] <element> [<voltag> [<vsn>] ]\n", 801 __progname, cname); 802 return 1; 803 } 804 805 static int 806 parse_element_type(char *cp) 807 { 808 int i; 809 810 for (i = 0; elements[i].et_name != NULL; ++i) 811 if (strcmp(elements[i].et_name, cp) == 0) 812 return (elements[i].et_type); 813 814 errx(1, "invalid element type `%s'", cp); 815 /* NOTREACHED */ 816 } 817 818 static const char * 819 element_type_name(int et) 820 { 821 int i; 822 823 for (i = 0; elements[i].et_name != NULL; i++) 824 if (elements[i].et_type == et) 825 return elements[i].et_name; 826 827 return "unknown"; 828 } 829 830 static int 831 parse_element_unit(char *cp) 832 { 833 int i; 834 char *p; 835 836 i = (int)strtol(cp, &p, 10); 837 if ((i < 0) || (*p != '\0')) 838 errx(1, "invalid unit number `%s'", cp); 839 840 return (i); 841 } 842 843 static int 844 parse_special(char *cp) 845 { 846 int val; 847 848 val = is_special(cp); 849 if (val) 850 return (val); 851 852 errx(1, "invalid modifier `%s'", cp); 853 /* NOTREACHED */ 854 } 855 856 static int 857 is_special(char *cp) 858 { 859 int i; 860 861 for (i = 0; specials[i].sw_name != NULL; ++i) 862 if (strcmp(specials[i].sw_name, cp) == 0) 863 return (specials[i].sw_value); 864 865 return (0); 866 } 867 868 static char * 869 bits_to_string(int v, const char *cp) 870 { 871 const char *np; 872 char f, sep, *bp; 873 static char buf[128]; 874 875 bp = buf; 876 (void) memset(buf, 0, sizeof(buf)); 877 878 for (sep = '<'; (f = *cp++) != 0; cp = np) { 879 for (np = cp; *np >= ' ';) 880 np++; 881 if ((v & (1 << (f - 1))) == 0) 882 continue; 883 bp += sprintf(bp, "%c%.*s", sep, (int)(long)(np - cp), cp); 884 sep = ','; 885 } 886 if (sep != '<') 887 *bp = '>'; 888 889 return (buf); 890 } 891 892 static void 893 cleanup() 894 { 895 /* Simple enough... */ 896 (void)close(changer_fd); 897 } 898 899 static void 900 usage() 901 { 902 903 (void) fprintf(stderr, "usage: %s command arg1 arg2 ...\n", __progname); 904 (void) fprintf(stderr, "Examples:\n"); 905 (void) fprintf(stderr, "\tchio -f /dev/ch0 move slot 1 drive 0\n"); 906 (void) fprintf(stderr, "\tchio ielem\n"); 907 (void) fprintf(stderr, "\tchio -f /dev/ch1 status\n"); 908 exit(1); 909 } 910