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 386 /* No arguments to this command. */ 387 388 ++argv; --argc; 389 390 if (argc) { 391 warnx("%s: no arguements expected", cname); 392 goto usage; 393 } 394 395 /* Get params from changer and display them. */ 396 (void) memset(&data, 0, sizeof(data)); 397 if (ioctl(changer_fd, CHIOGPARAMS, &data)) 398 err(1, "%s: CHIOGPARAMS", changer_name); 399 400 (void) printf("%s: %d slot%s, %d drive%s, %d picker%s", 401 changer_name, 402 data.cp_nslots, (data.cp_nslots > 1) ? "s" : "", 403 data.cp_ndrives, (data.cp_ndrives > 1) ? "s" : "", 404 data.cp_npickers, (data.cp_npickers > 1) ? "s" : ""); 405 if (data.cp_nportals) 406 (void) printf(", %d portal%s", data.cp_nportals, 407 (data.cp_nportals > 1) ? "s" : ""); 408 409 return (0); 410 411 usage: 412 (void) fprintf(stderr, "usage: %s %s\n", __progname, cname); 413 return (1); 414 } 415 416 /* ARGSUSED */ 417 static int 418 do_getpicker(char *cname, int argc, char **argv) 419 { 420 int picker; 421 422 /* No arguments to this command. */ 423 424 ++argv; --argc; 425 426 if (argc) { 427 warnx("%s: no arguments expected", cname); 428 goto usage; 429 } 430 431 /* Get current picker from changer and display it. */ 432 if (ioctl(changer_fd, CHIOGPICKER, &picker)) 433 err(1, "%s: CHIOGPICKER", changer_name); 434 435 (void) printf("%s: current picker: %d\n", changer_name, picker); 436 437 return (0); 438 439 usage: 440 (void) fprintf(stderr, "usage: %s %s\n", __progname, cname); 441 return (1); 442 } 443 444 static int 445 do_setpicker(char *cname, int argc, char **argv) 446 { 447 int picker; 448 449 ++argv; --argc; 450 451 if (argc < 1) { 452 warnx("%s: too few arguments", cname); 453 goto usage; 454 } else if (argc > 1) { 455 warnx("%s: too many arguments", cname); 456 goto usage; 457 } 458 459 picker = parse_element_unit(*argv); 460 461 /* Set the changer picker. */ 462 if (ioctl(changer_fd, CHIOSPICKER, &picker)) 463 err(1, "%s: CHIOSPICKER", changer_name); 464 465 return (0); 466 467 usage: 468 (void) fprintf(stderr, "usage: %s %s <picker>\n", __progname, cname); 469 return (1); 470 } 471 472 static int 473 do_status(char *cname, int argc, char **argv) 474 { 475 struct changer_params cp; 476 struct changer_element_status_request cesr; 477 int i, count, base, chet, schet, echet; 478 char *description; 479 int pvoltag = 0; 480 int avoltag = 0; 481 int sense = 0; 482 int scsi = 0; 483 int source = 0; 484 int intaddr = 0; 485 int c; 486 487 count = 0; 488 base = 0; 489 description = NULL; 490 491 optind = optreset = 1; 492 while ((c = getopt(argc, argv, "vVsSbaI")) != EOF) { 493 switch (c) { 494 case 'v': 495 pvoltag = 1; 496 break; 497 case 'V': 498 avoltag = 1; 499 break; 500 case 's': 501 sense = 1; 502 break; 503 case 'S': 504 source = 1; 505 break; 506 case 'b': 507 scsi = 1; 508 break; 509 case 'I': 510 intaddr = 1; 511 break; 512 case 'a': 513 pvoltag = avoltag = source = sense = scsi = intaddr = 1; 514 break; 515 default: 516 warnx("bad option", cname); 517 goto usage; 518 } 519 } 520 521 argc -= optind; 522 argv += optind; 523 524 /* 525 * On a status command, we expect the following: 526 * 527 * [<ET> [<start> [<end>] ] ] 528 * 529 * where ET == element type, start == first element to report, 530 * end == number of elements to report 531 * 532 * If we get no arguments, we get the status of all 533 * known element types. 534 */ 535 if (argc > 3) { 536 warnx("%s: too many arguments", cname); 537 goto usage; 538 } 539 540 /* 541 * Get params from changer. Specifically, we need the element 542 * counts. 543 */ 544 if (ioctl(changer_fd, CHIOGPARAMS, (char *)&cp)) 545 err(1, "%s: CHIOGPARAMS", changer_name); 546 547 if (argc > 0) 548 schet = echet = parse_element_type(argv[0]); 549 else { 550 schet = CHET_MT; 551 echet = CHET_DT; 552 } 553 if (argc > 1) { 554 base = atol(argv[1]); 555 count = 1; 556 } 557 if (argc > 2) 558 count = atol(argv[2]) - base + 1; 559 560 if (base < 0 || count < 0) 561 errx(1, "bad arguments"); 562 563 for (chet = schet; chet <= echet; ++chet) { 564 switch (chet) { 565 case CHET_MT: 566 if (count == 0) 567 count = cp.cp_npickers; 568 else if (count > cp.cp_npickers) 569 errx(1, "not that many pickers in device"); 570 description = "picker"; 571 break; 572 573 case CHET_ST: 574 if (count == 0) 575 count = cp.cp_nslots; 576 else if (count > cp.cp_nslots) 577 errx(1, "not that many slots in device"); 578 description = "slot"; 579 break; 580 581 case CHET_IE: 582 if (count == 0) 583 count = cp.cp_nportals; 584 else if (count > cp.cp_nportals) 585 errx(1, "not that many portals in device"); 586 description = "portal"; 587 break; 588 589 case CHET_DT: 590 if (count == 0) 591 count = cp.cp_ndrives; 592 else if (count > cp.cp_ndrives) 593 errx(1, "not that many drives in device"); 594 description = "drive"; 595 break; 596 597 default: 598 /* To appease gcc -Wuninitialized. */ 599 count = 0; 600 description = NULL; 601 } 602 603 if (count == 0) { 604 if (argc == 0) 605 continue; 606 else { 607 printf("%s: no %s elements\n", 608 changer_name, description); 609 return (0); 610 } 611 } 612 613 bzero(&cesr, sizeof(cesr)); 614 cesr.cesr_element_type = chet; 615 cesr.cesr_element_base = base; 616 cesr.cesr_element_count = count; 617 /* Allocate storage for the status structures. */ 618 cesr.cesr_element_status 619 = (struct changer_element_status *) 620 malloc(count * sizeof(struct changer_element_status)); 621 622 if (!cesr.cesr_element_status) 623 errx(1, "can't allocate status storage"); 624 625 if (avoltag || pvoltag) 626 cesr.cesr_flags |= CESR_VOLTAGS; 627 628 if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr)) { 629 free(cesr.cesr_element_status); 630 err(1, "%s: CHIOGSTATUS", changer_name); 631 } 632 633 /* Dump the status for each reported element. */ 634 for (i = 0; i < count; ++i) { 635 struct changer_element_status *ces = 636 &(cesr.cesr_element_status[i]); 637 printf("%s %d: %s", description, ces->ces_addr, 638 bits_to_string(ces->ces_flags, 639 CESTATUS_BITS)); 640 if (sense) 641 printf(" sense: <0x%02x/0x%02x>", 642 ces->ces_sensecode, 643 ces->ces_sensequal); 644 if (pvoltag) 645 printf(" voltag: <%s:%d>", 646 ces->ces_pvoltag.cv_volid, 647 ces->ces_pvoltag.cv_serial); 648 if (avoltag) 649 printf(" avoltag: <%s:%d>", 650 ces->ces_avoltag.cv_volid, 651 ces->ces_avoltag.cv_serial); 652 if (source) 653 if (ces->ces_flags & CES_SOURCE_VALID) 654 printf(" source: <%s %d>", 655 element_type_name( 656 ces->ces_source_type), 657 ces->ces_source_addr); 658 else 659 printf(" source: <>"); 660 if (intaddr) 661 printf(" intaddr: <%d>", ces->ces_int_addr); 662 if (scsi) { 663 printf(" scsi: <"); 664 if (ces->ces_flags & CES_SCSIID_VALID) 665 printf("%d", ces->ces_scsi_id); 666 else 667 putchar('?'); 668 putchar(':'); 669 if (ces->ces_flags & CES_LUN_VALID) 670 printf("%d", ces->ces_scsi_lun); 671 else 672 putchar('?'); 673 putchar('>'); 674 } 675 putchar('\n'); 676 } 677 678 free(cesr.cesr_element_status); 679 count = 0; 680 } 681 682 return (0); 683 684 usage: 685 (void) fprintf(stderr, "usage: %s %s [-vVsSbaA] [<element type> [<start-addr> [<end-addr>] ] ]\n", 686 __progname, cname); 687 return (1); 688 } 689 690 static int 691 do_ielem(char *cname, int argc, char **argv) 692 { 693 int timeout = 0; 694 695 if (argc == 2) { 696 timeout = atol(argv[1]); 697 } else if (argc > 1) { 698 warnx("%s: too many arguments", cname); 699 goto usage; 700 } 701 702 if (ioctl(changer_fd, CHIOIELEM, &timeout)) 703 err(1, "%s: CHIOIELEM", changer_name); 704 705 return (0); 706 707 usage: 708 (void) fprintf(stderr, "usage: %s %s [<timeout>]\n", 709 __progname, cname); 710 return (1); 711 } 712 713 static int 714 do_voltag(char *cname, int argc, char **argv) 715 { 716 int force = 0; 717 int clear = 0; 718 int alternate = 0; 719 int c; 720 struct changer_set_voltag_request csvr; 721 722 bzero(&csvr, sizeof(csvr)); 723 724 optind = optreset = 1; 725 while ((c = getopt(argc, argv, "fca")) != EOF) { 726 switch (c) { 727 case 'f': 728 force = 1; 729 break; 730 case 'c': 731 clear = 1; 732 break; 733 case 'a': 734 alternate = 1; 735 break; 736 default: 737 warnx("bad option", cname); 738 goto usage; 739 } 740 } 741 742 argc -= optind; 743 argv += optind; 744 745 if (argc < 2) { 746 warnx("missing element specification", cname); 747 goto usage; 748 } 749 750 csvr.csvr_type = parse_element_type(argv[0]); 751 csvr.csvr_addr = atol(argv[1]); 752 753 if (!clear) { 754 if (argc < 3 || argc > 4) { 755 warnx("missing argument", cname); 756 goto usage; 757 } 758 759 if (force) 760 csvr.csvr_flags = CSVR_MODE_REPLACE; 761 else 762 csvr.csvr_flags = CSVR_MODE_SET; 763 764 if (strlen(argv[2]) > sizeof(csvr.csvr_voltag.cv_volid)) { 765 warnx("volume label too long", cname); 766 goto usage; 767 } 768 769 strncpy(csvr.csvr_voltag.cv_volid, argv[2], 770 sizeof(csvr.csvr_voltag.cv_volid)); 771 772 if (argc == 4) { 773 csvr.csvr_voltag.cv_serial = atol(argv[3]); 774 } 775 } else { 776 if (argc != 2) { 777 warnx("unexpected argument", cname); 778 goto usage; 779 } 780 csvr.csvr_flags = CSVR_MODE_CLEAR; 781 } 782 783 if (alternate) { 784 csvr.csvr_flags |= CSVR_ALTERNATE; 785 } 786 787 if (ioctl(changer_fd, CHIOSETVOLTAG, &csvr)) 788 err(1, "%s: CHIOSETVOLTAG", changer_name); 789 790 return 0; 791 usage: 792 (void) fprintf(stderr, 793 "usage: %s %s [-fca] <element> [<voltag> [<vsn>] ]\n", 794 __progname, cname); 795 return 1; 796 } 797 798 static int 799 parse_element_type(char *cp) 800 { 801 int i; 802 803 for (i = 0; elements[i].et_name != NULL; ++i) 804 if (strcmp(elements[i].et_name, cp) == 0) 805 return (elements[i].et_type); 806 807 errx(1, "invalid element type `%s'", cp); 808 /* NOTREACHED */ 809 } 810 811 static const char * 812 element_type_name(int et) 813 { 814 int i; 815 816 for (i = 0; elements[i].et_name != NULL; i++) 817 if (elements[i].et_type == et) 818 return elements[i].et_name; 819 820 return "unknown"; 821 } 822 823 static int 824 parse_element_unit(char *cp) 825 { 826 int i; 827 char *p; 828 829 i = (int)strtol(cp, &p, 10); 830 if ((i < 0) || (*p != '\0')) 831 errx(1, "invalid unit number `%s'", cp); 832 833 return (i); 834 } 835 836 static int 837 parse_special(char *cp) 838 { 839 int val; 840 841 val = is_special(cp); 842 if (val) 843 return (val); 844 845 errx(1, "invalid modifier `%s'", cp); 846 /* NOTREACHED */ 847 } 848 849 static int 850 is_special(char *cp) 851 { 852 int i; 853 854 for (i = 0; specials[i].sw_name != NULL; ++i) 855 if (strcmp(specials[i].sw_name, cp) == 0) 856 return (specials[i].sw_value); 857 858 return (0); 859 } 860 861 static char * 862 bits_to_string(int v, const char *cp) 863 { 864 const char *np; 865 char f, sep, *bp; 866 static char buf[128]; 867 868 bp = buf; 869 (void) memset(buf, 0, sizeof(buf)); 870 871 for (sep = '<'; (f = *cp++) != 0; cp = np) { 872 for (np = cp; *np >= ' ';) 873 np++; 874 if ((v & (1 << (f - 1))) == 0) 875 continue; 876 bp += sprintf(bp, "%c%.*s", sep, (int)(long)(np - cp), cp); 877 sep = ','; 878 } 879 if (sep != '<') 880 *bp = '>'; 881 882 return (buf); 883 } 884 885 static void 886 cleanup() 887 { 888 /* Simple enough... */ 889 (void)close(changer_fd); 890 } 891 892 static void 893 usage() 894 { 895 896 (void) fprintf(stderr, "usage: %s command arg1 arg2 ...\n", __progname); 897 (void) fprintf(stderr, "Examples:\n"); 898 (void) fprintf(stderr, "\tchio -f /dev/ch0 move slot 1 drive 0\n"); 899 (void) fprintf(stderr, "\tchio ielem\n"); 900 (void) fprintf(stderr, "\tchio -f /dev/ch1 status\n"); 901 exit(1); 902 } 903