1 /* $NetBSD: chio.c,v 1.27 2005/06/01 15:20:04 lukem Exp $ */ 2 3 /*- 4 * Copyright (c) 1996, 1998, 1999 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 9 * NASA Ames Research Center. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the NetBSD 22 * Foundation, Inc. and its contributors. 23 * 4. Neither the name of The NetBSD Foundation nor the names of its 24 * contributors may be used to endorse or promote products derived 25 * from this software without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 */ 39 40 /* 41 * Additional Copyright (c) 1997, by Matthew Jacob, for NASA/Ames Research Ctr. 42 */ 43 44 #include <sys/cdefs.h> 45 #ifndef lint 46 __COPYRIGHT( 47 "@(#) Copyright (c) 1996, 1998, 1999\ 48 The NetBSD Foundation, Inc. All rights reserved."); 49 __RCSID("$NetBSD: chio.c,v 1.27 2005/06/01 15:20:04 lukem Exp $"); 50 #endif 51 52 #include <sys/param.h> 53 #include <sys/ioctl.h> 54 #include <sys/chio.h> 55 #include <sys/cdio.h> /* for ATAPI CD changer; too bad it uses a lame API */ 56 57 #include <ctype.h> 58 #include <err.h> 59 #include <errno.h> 60 #include <fcntl.h> 61 #include <limits.h> 62 #include <stdio.h> 63 #include <stdlib.h> 64 #include <string.h> 65 #include <unistd.h> 66 67 #include "defs.h" 68 #include "pathnames.h" 69 70 int main(int, char *[]); 71 static void usage(void); 72 static void cleanup(void); 73 static int parse_element_type(const char *); 74 static int parse_element_unit(const char *); 75 static int parse_special(const char *); 76 static int is_special(const char *); 77 static const char *bits_to_string(int, const char *); 78 79 static int do_move(const char *, int, char **); 80 static int do_exchange(const char *, int, char **); 81 static int do_position(const char *, int, char **); 82 static int do_params(const char *, int, char **); 83 static int do_getpicker(const char *, int, char **); 84 static int do_setpicker(const char *, int, char **); 85 static int do_status(const char *, int, char **); 86 static int do_ielem(const char *, int, char **); 87 static int do_cdlu(const char *, int, char **); 88 89 /* Valid changer element types. */ 90 const struct element_type elements[] = { 91 { "picker", CHET_MT }, 92 { "slot", CHET_ST }, 93 { "portal", CHET_IE }, 94 { "drive", CHET_DT }, 95 { NULL, 0 }, 96 }; 97 98 /* Valid commands. */ 99 const struct changer_command commands[] = { 100 { "move", " <from ET> <from EU> <to ET> <to EU> [inv]", 101 do_move }, 102 103 { "exchange", " <src ET> <src EU> <dst1 ET> <dst1 EU>\n" 104 "\t\t [<dst2 ET> <dst2 EU>] [inv1] [inv2]", 105 do_exchange }, 106 107 { "position", " <to ET> <to EU> [inv]", do_position }, 108 109 { "params", "", 110 do_params }, 111 112 { "getpicker", "", 113 do_getpicker }, 114 115 { "setpicker", " <picker>", 116 do_setpicker }, 117 118 { "status", " [<ET> [unit [count]]] [voltags]", 119 do_status }, 120 121 { "ielem", "", 122 do_ielem }, 123 124 { "cdlu", " load|unload <slot>\n" 125 "\t abort", 126 do_cdlu }, 127 128 { NULL, NULL, 129 NULL }, 130 }; 131 132 /* Valid special words. */ 133 const struct special_word specials[] = { 134 { "inv", SW_INVERT }, 135 { "inv1", SW_INVERT1 }, 136 { "inv2", SW_INVERT2 }, 137 { "voltags", SW_VOLTAGS }, 138 { NULL, 0 }, 139 }; 140 141 static const char *changer_name; 142 static int changer_fd; 143 144 int 145 main(int argc, char *argv[]) 146 { 147 int ch, i; 148 149 setprogname(argv[0]); 150 while ((ch = getopt(argc, argv, "f:")) != -1) { 151 switch (ch) { 152 case 'f': 153 changer_name = optarg; 154 break; 155 default: 156 usage(); 157 /* NOTREACHED */ 158 } 159 } 160 argc -= optind; 161 argv += optind; 162 163 if (argc == 0) 164 usage(); 165 /* NOTREACHED */ 166 167 /* Get the default changer if not already specified. */ 168 if (changer_name == NULL) 169 if ((changer_name = getenv(CHANGER_ENV_VAR)) == NULL) 170 changer_name = _PATH_CH; 171 172 /* Open the changer device. */ 173 if ((changer_fd = open(changer_name, O_RDWR, 0600)) == -1) 174 err(EXIT_FAILURE, "%s: open", changer_name); 175 /* NOTREACHED */ 176 177 /* Register cleanup function. */ 178 if (atexit(cleanup)) 179 err(EXIT_FAILURE, "can't register cleanup function"); 180 /* NOTREACHED */ 181 182 /* Find the specified command. */ 183 for (i = 0; commands[i].cc_name != NULL; ++i) 184 if (strcmp(*argv, commands[i].cc_name) == 0) 185 break; 186 if (commands[i].cc_name == NULL) 187 errx(EXIT_FAILURE, "unknown command: %s", *argv); 188 /* NOTREACHED */ 189 190 /* Skip over the command name and call handler. */ 191 ++argv; --argc; 192 exit((*commands[i].cc_handler)(commands[i].cc_name, argc, argv)); 193 /* NOTREACHED */ 194 } 195 196 static int 197 do_move(const char *cname, int argc, char **argv) 198 { 199 struct changer_move_request cmd; 200 int val; 201 202 /* 203 * On a move command, we expect the following: 204 * 205 * <from ET> <from EU> <to ET> <to EU> [inv] 206 * 207 * where ET == element type and EU == element unit. 208 */ 209 if (argc < 4) { 210 warnx("%s: too few arguments", cname); 211 usage(); 212 /*NOTREACHED*/ 213 } else if (argc > 5) { 214 warnx("%s: too many arguments", cname); 215 usage(); 216 /*NOTREACHED*/ 217 } 218 (void)memset(&cmd, 0, sizeof(cmd)); 219 220 /* <from ET> */ 221 cmd.cm_fromtype = parse_element_type(*argv); 222 ++argv; --argc; 223 224 /* <from EU> */ 225 cmd.cm_fromunit = parse_element_unit(*argv); 226 ++argv; --argc; 227 228 /* <to ET> */ 229 cmd.cm_totype = parse_element_type(*argv); 230 ++argv; --argc; 231 232 /* <to EU> */ 233 cmd.cm_tounit = parse_element_unit(*argv); 234 ++argv; --argc; 235 236 /* Deal with optional command modifier. */ 237 if (argc) { 238 val = parse_special(*argv); 239 switch (val) { 240 case SW_INVERT: 241 cmd.cm_flags |= CM_INVERT; 242 break; 243 default: 244 errx(EXIT_FAILURE, "%s: inappropriate modifier `%s'", 245 cname, *argv); 246 /* NOTREACHED */ 247 } 248 } 249 250 /* Send command to changer. */ 251 if (ioctl(changer_fd, CHIOMOVE, &cmd)) 252 err(EXIT_FAILURE, "%s: CHIOMOVE", changer_name); 253 /* NOTREACHED */ 254 255 return (0); 256 } 257 258 static int 259 do_exchange(const char *cname, int argc, char **argv) 260 { 261 struct changer_exchange_request cmd; 262 int val; 263 264 /* 265 * On an exchange command, we expect the following: 266 * 267 * <src ET> <src EU> <dst1 ET> <dst1 EU> [<dst2 ET> <dst2 EU>] [inv1] [inv2] 268 * 269 * where ET == element type and EU == element unit. 270 */ 271 if (argc < 4) { 272 warnx("%s: too few arguments", cname); 273 usage(); 274 /*NOTREACHED*/ 275 } else if (argc > 8) { 276 warnx("%s: too many arguments", cname); 277 usage(); 278 /*NOTREACHED*/ 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 /* <src EU> */ 287 cmd.ce_srcunit = parse_element_unit(*argv); 288 ++argv; --argc; 289 290 /* <dst1 ET> */ 291 cmd.ce_fdsttype = parse_element_type(*argv); 292 ++argv; --argc; 293 294 /* <dst1 EU> */ 295 cmd.ce_fdstunit = parse_element_unit(*argv); 296 ++argv; --argc; 297 298 /* 299 * If the next token is a special word or there are no more 300 * arguments, then this is a case of simple exchange. 301 * dst2 == src. 302 */ 303 if ((argc == 0) || is_special(*argv)) { 304 cmd.ce_sdsttype = cmd.ce_srctype; 305 cmd.ce_sdstunit = cmd.ce_srcunit; 306 goto do_special; 307 } 308 309 /* <dst2 ET> */ 310 cmd.ce_sdsttype = parse_element_type(*argv); 311 ++argv; --argc; 312 313 /* <dst2 EU> */ 314 cmd.ce_sdstunit = parse_element_unit(*argv); 315 ++argv; --argc; 316 317 do_special: 318 /* Deal with optional command modifiers. */ 319 while (argc) { 320 val = parse_special(*argv); 321 ++argv; --argc; 322 switch (val) { 323 case SW_INVERT1: 324 cmd.ce_flags |= CE_INVERT1; 325 break; 326 case SW_INVERT2: 327 cmd.ce_flags |= CE_INVERT2; 328 break; 329 default: 330 errx(EXIT_FAILURE, "%s: inappropriate modifier `%s'", 331 cname, *argv); 332 /* NOTREACHED */ 333 } 334 } 335 336 /* Send command to changer. */ 337 if (ioctl(changer_fd, CHIOEXCHANGE, &cmd)) 338 err(EXIT_FAILURE, "%s: CHIOEXCHANGE", changer_name); 339 /* NOTREACHED */ 340 341 return (0); 342 } 343 344 static int 345 do_position(const char *cname, int argc, char **argv) 346 { 347 struct changer_position_request cmd; 348 int val; 349 350 /* 351 * On a position command, we expect the following: 352 * 353 * <to ET> <to EU> [inv] 354 * 355 * where ET == element type and EU == element unit. 356 */ 357 if (argc < 2) { 358 warnx("%s: too few arguments", cname); 359 usage(); 360 /*NOTREACHED*/ 361 } else if (argc > 3) { 362 warnx("%s: too many arguments", cname); 363 usage(); 364 /*NOTREACHED*/ 365 } 366 (void)memset(&cmd, 0, sizeof(cmd)); 367 368 /* <to ET> */ 369 cmd.cp_type = parse_element_type(*argv); 370 ++argv; --argc; 371 372 /* <to EU> */ 373 cmd.cp_unit = parse_element_unit(*argv); 374 ++argv; --argc; 375 376 /* Deal with optional command modifier. */ 377 if (argc) { 378 val = parse_special(*argv); 379 switch (val) { 380 case SW_INVERT: 381 cmd.cp_flags |= CP_INVERT; 382 break; 383 default: 384 errx(EXIT_FAILURE, "%s: inappropriate modifier `%s'", 385 cname, *argv); 386 /* NOTREACHED */ 387 } 388 } 389 390 /* Send command to changer. */ 391 if (ioctl(changer_fd, CHIOPOSITION, &cmd)) 392 err(EXIT_FAILURE, "%s: CHIOPOSITION", changer_name); 393 /* NOTREACHED */ 394 395 return (0); 396 } 397 398 /* ARGSUSED */ 399 static int 400 do_params(const char *cname, int argc, char **argv) 401 { 402 struct changer_params data; 403 404 /* No arguments to this command. */ 405 if (argc) { 406 warnx("%s: no arguements expected", cname); 407 usage(); 408 /* NOTREACHED */ 409 } 410 411 /* Get params from changer and display them. */ 412 (void)memset(&data, 0, sizeof(data)); 413 if (ioctl(changer_fd, CHIOGPARAMS, &data)) 414 err(EXIT_FAILURE, "%s: CHIOGPARAMS", changer_name); 415 /* NOTREACHED */ 416 417 #define PLURAL(n) (n) > 1 ? "s" : "" 418 419 (void)printf("%s: %d slot%s, %d drive%s, %d picker%s", 420 changer_name, 421 data.cp_nslots, PLURAL(data.cp_nslots), 422 data.cp_ndrives, PLURAL(data.cp_ndrives), 423 data.cp_npickers, PLURAL(data.cp_npickers)); 424 if (data.cp_nportals) 425 (void)printf(", %d portal%s", data.cp_nportals, 426 PLURAL(data.cp_nportals)); 427 428 #undef PLURAL 429 430 (void)printf("\n%s: current picker: %d\n", changer_name, data.cp_curpicker); 431 432 return (0); 433 } 434 435 /* ARGSUSED */ 436 static int 437 do_getpicker(const char *cname, int argc, char **argv) 438 { 439 int picker; 440 441 /* No arguments to this command. */ 442 if (argc) { 443 warnx("%s: no arguments expected", cname); 444 usage(); 445 /*NOTREACHED*/ 446 } 447 448 /* Get current picker from changer and display it. */ 449 if (ioctl(changer_fd, CHIOGPICKER, &picker)) 450 err(EXIT_FAILURE, "%s: CHIOGPICKER", changer_name); 451 /* NOTREACHED */ 452 453 (void)printf("%s: current picker: %d\n", changer_name, picker); 454 455 return (0); 456 } 457 458 static int 459 do_setpicker(const char *cname, int argc, char **argv) 460 { 461 int picker; 462 463 if (argc < 1) { 464 warnx("%s: too few arguments", cname); 465 usage(); 466 /*NOTREACHED*/ 467 } else if (argc > 1) { 468 warnx("%s: too many arguments", cname); 469 usage(); 470 /*NOTREACHED*/ 471 } 472 473 picker = parse_element_unit(*argv); 474 475 /* Set the changer picker. */ 476 if (ioctl(changer_fd, CHIOSPICKER, &picker)) 477 err(EXIT_FAILURE, "%s: CHIOSPICKER", changer_name); 478 479 return (0); 480 } 481 482 static int 483 do_status(const char *cname, int argc, char **argv) 484 { 485 struct changer_element_status_request cmd; 486 struct changer_params data; 487 struct changer_element_status *ces; 488 int i, chet, count, echet, flags, have_ucount, have_unit; 489 int schet, ucount, unit; 490 size_t size; 491 492 flags = 0; 493 ucount = 0; 494 unit = 0; 495 have_ucount = 0; 496 have_unit = 0; 497 498 /* 499 * On a status command, we expect the following: 500 * 501 * [<ET> [unit [count]]] [voltags] 502 * 503 * where ET == element type. 504 * 505 * If we get no element-related arguments, we get the status of all 506 * known element types. 507 */ 508 if (argc > 4) { 509 warnx("%s: too many arguments", cname); 510 usage(); 511 /*NOTREACHED*/ 512 } 513 514 /* 515 * Get params from changer. Specifically, we need the element 516 * counts. 517 */ 518 (void)memset(&data, 0, sizeof(data)); 519 if (ioctl(changer_fd, CHIOGPARAMS, &data)) 520 err(EXIT_FAILURE, "%s: CHIOGPARAMS", changer_name); 521 /* NOTREACHED */ 522 523 schet = CHET_MT; 524 echet = CHET_DT; 525 526 for (; argc != 0; argc--, argv++) { 527 /* 528 * If we have the voltags modifier, it must be the 529 * last argument. 530 */ 531 if (is_special(argv[0])) { 532 if (argc != 1) { 533 warnx("%s: malformed command line", cname); 534 usage(); 535 /*NOTREACHED*/ 536 } 537 if (parse_special(argv[0]) != SW_VOLTAGS) 538 errx(EXIT_FAILURE, 539 "%s: inappropriate special word: %s", 540 cname, argv[0]); 541 /* NOTREACHED */ 542 flags |= CESR_VOLTAGS; 543 continue; 544 } 545 546 /* 547 * If we get an element type, we can't have specified 548 * anything else. 549 */ 550 if (isdigit((unsigned char)*argv[0]) == 0) { 551 if (schet == echet || flags != 0 || have_unit || 552 have_ucount) { 553 warnx("%s: malformed command line", cname); 554 usage(); 555 /*NOTREACHED*/ 556 } 557 schet = echet = parse_element_type(argv[0]); 558 continue; 559 } 560 561 /* 562 * We know we have a digit here. If we do, we must 563 * have specified an element type. 564 */ 565 if (schet != echet) { 566 warnx("%s: malformed command line", cname); 567 usage(); 568 /*NOTREACHED*/ 569 } 570 571 i = parse_element_unit(argv[0]); 572 573 if (have_unit == 0) { 574 unit = i; 575 have_unit = 1; 576 } else if (have_ucount == 0) { 577 ucount = i; 578 have_ucount = 1; 579 } else { 580 warnx("%s: malformed command line", cname); 581 usage(); 582 /*NOTREACHED*/ 583 } 584 } 585 586 for (chet = schet; chet <= echet; ++chet) { 587 switch (chet) { 588 case CHET_MT: 589 count = data.cp_npickers; 590 break; 591 case CHET_ST: 592 count = data.cp_nslots; 593 break; 594 case CHET_IE: 595 count = data.cp_nportals; 596 break; 597 case CHET_DT: 598 count = data.cp_ndrives; 599 break; 600 default: 601 /* To appease gcc -Wuninitialized. */ 602 count = 0; 603 } 604 605 if (count == 0) { 606 if (schet != echet) 607 continue; 608 else { 609 (void)printf("%s: no %s elements\n", 610 changer_name, 611 elements[chet].et_name); 612 return (0); 613 } 614 } 615 616 /* 617 * If we have a unit, we may or may not have a count. 618 * If we don't have a unit, we don't have a count, either. 619 * 620 * Make sure both are initialized. 621 */ 622 if (have_unit) { 623 if (have_ucount == 0) 624 ucount = 1; 625 } else { 626 unit = 0; 627 ucount = count; 628 } 629 630 if ((unit + ucount) > count) 631 errx(EXIT_FAILURE, "%s: unvalid unit/count %d/%d", 632 cname, unit, ucount); 633 /* NOTREACHED */ 634 635 size = ucount * sizeof(struct changer_element_status); 636 637 /* Allocate storage for the status bytes. */ 638 if ((ces = malloc(size)) == NULL) 639 errx(EXIT_FAILURE, "can't allocate status storage"); 640 /* NOTREACHED */ 641 642 (void)memset(ces, 0, size); 643 (void)memset(&cmd, 0, sizeof(cmd)); 644 645 cmd.cesr_type = chet; 646 cmd.cesr_unit = unit; 647 cmd.cesr_count = ucount; 648 cmd.cesr_flags = flags; 649 cmd.cesr_data = ces; 650 651 /* 652 * Should we deal with this eventually? 653 */ 654 cmd.cesr_vendor_data = NULL; 655 656 if (ioctl(changer_fd, CHIOGSTATUS, &cmd)) { 657 free(ces); 658 err(EXIT_FAILURE, "%s: CHIOGSTATUS", changer_name); 659 /* NOTREACHED */ 660 } 661 662 /* Dump the status for each element of this type. */ 663 for (i = 0; i < ucount; i++) { 664 (void)printf("%s %d: ", elements[chet].et_name, 665 unit + i); 666 if ((ces[i].ces_flags & CESTATUS_STATUS_VALID) == 0) { 667 (void)printf("status not available\n"); 668 continue; 669 } 670 (void)printf("%s", bits_to_string(ces[i].ces_flags, 671 CESTATUS_BITS)); 672 if (ces[i].ces_flags & CESTATUS_XNAME_VALID) 673 (void)printf(" (%s)", ces[i].ces_xname); 674 (void)printf("\n"); 675 if (ces[i].ces_flags & CESTATUS_PVOL_VALID) 676 (void)printf("\tPrimary volume tag: %s " 677 "ver. %d\n", 678 ces[i].ces_pvoltag.cv_tag, 679 ces[i].ces_pvoltag.cv_serial); 680 if (ces[i].ces_flags & CESTATUS_AVOL_VALID) 681 (void)printf("\tAlternate volume tag: %s " 682 "ver. %d\n", 683 ces[i].ces_avoltag.cv_tag, 684 ces[i].ces_avoltag.cv_serial); 685 if (ces[i].ces_flags & CESTATUS_FROM_VALID) 686 (void)printf("\tFrom: %s %d\n", 687 elements[ces[i].ces_from_type].et_name, 688 ces[i].ces_from_unit); 689 if (ces[i].ces_vendor_len) 690 (void)printf("\tVendor-specific data size: " 691 "%lu\n", (u_long)ces[i].ces_vendor_len); 692 } 693 free(ces); 694 } 695 696 return (0); 697 } 698 699 /* ARGSUSED */ 700 static int 701 do_ielem(const char *cname, int argc, char **argv) 702 { 703 704 if (ioctl(changer_fd, CHIOIELEM, NULL)) 705 err(EXIT_FAILURE, "%s: CHIOIELEM", changer_name); 706 /* NOTREACHED */ 707 708 return (0); 709 } 710 711 /* ARGSUSED */ 712 static int 713 do_cdlu(const char *cname, int argc, char **argv) 714 { 715 static const struct special_word cdlu_subcmds[] = { 716 { "load", CD_LU_LOAD }, 717 { "unload", CD_LU_UNLOAD }, 718 { "abort", CD_LU_ABORT }, 719 { NULL, 0 }, 720 }; 721 struct ioc_load_unload cmd; 722 int i; 723 724 /* 725 * This command is a little different, since we are mostly dealing 726 * with ATAPI CD changers, which have a lame API (since ATAPI doesn't 727 * have LUNs). 728 * 729 * We have 3 sub-commands: "load", "unload", and "abort". The 730 * first two take a slot number. The latter does not. 731 */ 732 733 if (argc < 1 || argc > 2) 734 usage(); 735 /*NOTREACHED*/ 736 737 for (i = 0; cdlu_subcmds[i].sw_name != NULL; i++) { 738 if (strcmp(argv[0], cdlu_subcmds[i].sw_name) == 0) { 739 cmd.options = cdlu_subcmds[i].sw_value; 740 break; 741 } 742 } 743 if (cdlu_subcmds[i].sw_name == NULL) 744 usage(); 745 /*NOTREACHED*/ 746 747 if (strcmp(argv[0], "abort") == 0) 748 cmd.slot = 0; 749 else 750 cmd.slot = parse_element_unit(argv[1]); 751 752 /* 753 * XXX Should maybe do something different with the device 754 * XXX handling for cdlu; think about this some more. 755 */ 756 if (ioctl(changer_fd, CDIOCLOADUNLOAD, &cmd)) 757 err(EXIT_FAILURE, "%s: CDIOCLOADUNLOAD", changer_name); 758 /* NOTREACHED */ 759 760 return (0); 761 } 762 763 static int 764 parse_element_type(const char *cp) 765 { 766 int i; 767 768 for (i = 0; elements[i].et_name != NULL; ++i) 769 if (strcmp(elements[i].et_name, cp) == 0) 770 return (elements[i].et_type); 771 772 errx(EXIT_FAILURE, "invalid element type `%s'", cp); 773 /* NOTREACHED */ 774 } 775 776 static int 777 parse_element_unit(const char *cp) 778 { 779 char *p; 780 int i; 781 782 i = (int)strtol(cp, &p, 10); 783 if ((i < 0) || (*p != '\0')) 784 errx(EXIT_FAILURE, "invalid unit number `%s'", cp); 785 786 return (i); 787 } 788 789 static int 790 parse_special(const char *cp) 791 { 792 int val; 793 794 val = is_special(cp); 795 if (val) 796 return (val); 797 798 errx(EXIT_FAILURE, "invalid modifier `%s'", cp); 799 /* NOTREACHED */ 800 } 801 802 static int 803 is_special(const char *cp) 804 { 805 int i; 806 807 for (i = 0; specials[i].sw_name != NULL; ++i) 808 if (strcmp(specials[i].sw_name, cp) == 0) 809 return (specials[i].sw_value); 810 811 return (0); 812 } 813 814 static const char * 815 bits_to_string(int v, const char *cp) 816 { 817 static char buf[128]; 818 const char *np; 819 char *bp, f; 820 int first; 821 822 bp = buf; 823 *bp++ = '<'; 824 for (first = 1; (f = *cp++) != 0; cp = np) { 825 for (np = cp; *np >= ' ';) 826 np++; 827 if ((v & (1 << (f - 1))) == 0) 828 continue; 829 if (first) 830 first = 0; 831 else 832 *bp++ = ','; 833 (void)memcpy(bp, cp, np - cp); 834 bp += np - cp; 835 } 836 *bp++ = '>'; 837 *bp = '\0'; 838 839 return (buf); 840 } 841 842 static void 843 cleanup(void) 844 { 845 846 /* Simple enough... */ 847 (void)close(changer_fd); 848 } 849 850 static void 851 usage(void) 852 { 853 int i; 854 855 (void)fprintf(stderr, 856 "usage: %s [-f changer] command arg1 arg2 [arg3 [...]]\n", 857 getprogname()); 858 859 (void)fprintf(stderr, "Where command (and args) are:\n"); 860 for (i = 0; commands[i].cc_name != NULL; i++) 861 (void)fprintf(stderr, "\t%s%s\n", commands[i].cc_name, 862 commands[i].cc_args); 863 exit(1); 864 /* NOTREACHED */ 865 } 866