1 /* $Id: $ */ 2 3 /* 4 * Copyright (c) 1996 Jason R. Thorpe <thorpej@and.com> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgements: 17 * This product includes software developed by Jason R. Thorpe 18 * for And Communications, http://www.and.com/ 19 * 4. The name of the author may not be used to endorse or promote products 20 * derived from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/param.h> 36 #include <sys/ioctl.h> 37 #include <sys/chio.h> 38 #include <err.h> 39 #include <errno.h> 40 #include <fcntl.h> 41 #include <limits.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <unistd.h> 46 47 #include "defs.h" 48 #include "pathnames.h" 49 50 extern char *__progname; /* from crt0.o */ 51 52 static void usage __P((void)); 53 static void cleanup __P((void)); 54 static int parse_element_type __P((char *)); 55 static int parse_element_unit __P((char *)); 56 static int parse_special __P((char *)); 57 static int is_special __P((char *)); 58 static char *bits_to_string __P((int, const char *)); 59 60 static int do_move __P((char *, int, char **)); 61 static int do_exchange __P((char *, int, char **)); 62 static int do_position __P((char *, int, char **)); 63 static int do_params __P((char *, int, char **)); 64 static int do_getpicker __P((char *, int, char **)); 65 static int do_setpicker __P((char *, int, char **)); 66 static int do_status __P((char *, int, char **)); 67 68 /* Valid changer element types. */ 69 const struct element_type elements[] = { 70 { "picker", CHET_MT }, 71 { "slot", CHET_ST }, 72 { "portal", CHET_IE }, 73 { "drive", CHET_DT }, 74 { NULL, 0 }, 75 }; 76 77 /* Valid commands. */ 78 const struct changer_command commands[] = { 79 { "move", do_move }, 80 { "exchange", do_exchange }, 81 { "position", do_position }, 82 { "params", do_params }, 83 { "getpicker", do_getpicker }, 84 { "setpicker", do_setpicker }, 85 { "status", do_status }, 86 { NULL, 0 }, 87 }; 88 89 /* Valid special words. */ 90 const struct special_word specials[] = { 91 { "inv", SW_INVERT }, 92 { "inv1", SW_INVERT1 }, 93 { "inv2", SW_INVERT2 }, 94 { NULL, 0 }, 95 }; 96 97 static int changer_fd; 98 static char *changer_name; 99 100 int 101 main(argc, argv) 102 int argc; 103 char **argv; 104 { 105 int ch, i; 106 char *cp; 107 108 while ((ch = getopt(argc, argv, "f:")) != -1) { 109 switch (ch) { 110 case 'f': 111 changer_name = optarg; 112 break; 113 114 default: 115 usage(); 116 } 117 } 118 argc -= optind; 119 argv += optind; 120 121 if (argc == 0) 122 usage(); 123 124 /* Get the default changer if not already specified. */ 125 if (changer_name == NULL) 126 if ((changer_name = getenv(CHANGER_ENV_VAR)) == NULL) 127 changer_name = _PATH_CH; 128 129 /* Open the changer device. */ 130 if ((changer_fd = open(changer_name, O_RDWR, 0600)) == -1) 131 err(1, "%s: open", changer_name); 132 133 /* Register cleanup function. */ 134 if (atexit(cleanup)) 135 err(1, "can't register cleanup function"); 136 137 /* Find the specified command. */ 138 for (i = 0; commands[i].cc_name != NULL; ++i) 139 if (strcmp(*argv, commands[i].cc_name) == 0) 140 break; 141 if (commands[i].cc_name == NULL) 142 errx(1, "unknown command: %s", *argv); 143 144 /* Skip over the command name and call handler. */ 145 ++argv; --argc; 146 exit ((*commands[i].cc_handler)(commands[i].cc_name, argc, argv)); 147 } 148 149 static int 150 do_move(cname, argc, argv) 151 char *cname; 152 int argc; 153 char **argv; 154 { 155 struct changer_move cmd; 156 int val; 157 158 /* 159 * On a move command, we expect the following: 160 * 161 * <from ET> <from EU> <to ET> <to EU> [inv] 162 * 163 * where ET == element type and EU == element unit. 164 */ 165 if (argc < 4) { 166 warnx("%s: too few arguments", cname); 167 goto usage; 168 } else if (argc > 5) { 169 warnx("%s: too many arguments", cname); 170 goto usage; 171 } 172 bzero(&cmd, sizeof(cmd)); 173 174 /* <from ET> */ 175 cmd.cm_fromtype = parse_element_type(*argv); 176 ++argv; --argc; 177 178 /* <from EU> */ 179 cmd.cm_fromunit = parse_element_unit(*argv); 180 ++argv; --argc; 181 182 /* <to ET> */ 183 cmd.cm_totype = parse_element_type(*argv); 184 ++argv; --argc; 185 186 /* <to EU> */ 187 cmd.cm_tounit = parse_element_unit(*argv); 188 ++argv; --argc; 189 190 /* Deal with optional command modifier. */ 191 if (argc) { 192 val = parse_special(*argv); 193 switch (val) { 194 case SW_INVERT: 195 cmd.cm_flags |= CM_INVERT; 196 break; 197 198 default: 199 errx(1, "%s: inappropriate modifier `%s'", 200 cname, *argv); 201 /* NOTREACHED */ 202 } 203 } 204 205 /* Send command to changer. */ 206 if (ioctl(changer_fd, CHIOMOVE, (char *)&cmd)) 207 err(1, "%s: CHIOMOVE", changer_name); 208 209 return (0); 210 211 usage: 212 fprintf(stderr, "usage: %s %s " 213 "<from ET> <from EU> <to ET> <to EU> [inv]\n", __progname, cname); 214 return (1); 215 } 216 217 static int 218 do_exchange(cname, argc, argv) 219 char *cname; 220 int argc; 221 char **argv; 222 { 223 struct changer_exchange cmd; 224 int val; 225 226 /* 227 * On an exchange command, we expect the following: 228 * 229 * <src ET> <src EU> <dst1 ET> <dst1 EU> [<dst2 ET> <dst2 EU>] [inv1] [inv2] 230 * 231 * where ET == element type and EU == element unit. 232 */ 233 if (argc < 4) { 234 warnx("%s: too few arguments", cname); 235 goto usage; 236 } else if (argc > 8) { 237 warnx("%s: too many arguments", cname); 238 goto usage; 239 } 240 bzero(&cmd, sizeof(cmd)); 241 242 /* <src ET> */ 243 cmd.ce_srctype = parse_element_type(*argv); 244 ++argv; --argc; 245 246 /* <src EU> */ 247 cmd.ce_srcunit = parse_element_unit(*argv); 248 ++argv; --argc; 249 250 /* <dst1 ET> */ 251 cmd.ce_fdsttype = parse_element_type(*argv); 252 ++argv; --argc; 253 254 /* <dst1 EU> */ 255 cmd.ce_fdstunit = parse_element_unit(*argv); 256 ++argv; --argc; 257 258 /* 259 * If the next token is a special word or there are no more 260 * arguments, then this is a case of simple exchange. 261 * dst2 == src. 262 */ 263 if ((argc == 0) || is_special(*argv)) { 264 cmd.ce_sdsttype = cmd.ce_srctype; 265 cmd.ce_sdstunit = cmd.ce_srcunit; 266 goto do_special; 267 } 268 269 /* <dst2 ET> */ 270 cmd.ce_sdsttype = parse_element_type(*argv); 271 ++argv; --argc; 272 273 /* <dst2 EU> */ 274 cmd.ce_sdstunit = parse_element_unit(*argv); 275 ++argv; --argc; 276 277 do_special: 278 /* Deal with optional command modifiers. */ 279 while (argc) { 280 val = parse_special(*argv); 281 ++argv; --argc; 282 switch (val) { 283 case SW_INVERT1: 284 cmd.ce_flags |= CE_INVERT1; 285 break; 286 287 case SW_INVERT2: 288 cmd.ce_flags |= CE_INVERT2; 289 break; 290 291 default: 292 errx(1, "%s: inappropriate modifier `%s'", 293 cname, *argv); 294 /* NOTREACHED */ 295 } 296 } 297 298 /* Send command to changer. */ 299 if (ioctl(changer_fd, CHIOEXCHANGE, (char *)&cmd)) 300 err(1, "%s: CHIOEXCHANGE", changer_name); 301 302 return (0); 303 304 usage: 305 fprintf(stderr, "usage: %s %s <src ET> <src EU> <dst1 ET> <dst1 EU>\n" 306 " [<dst2 ET> <dst2 EU>] [inv1] [inv2]\n", 307 __progname, cname); 308 return (1); 309 } 310 311 static int 312 do_position(cname, argc, argv) 313 char *cname; 314 int argc; 315 char **argv; 316 { 317 struct changer_position cmd; 318 int val; 319 320 /* 321 * On a position command, we expect the following: 322 * 323 * <to ET> <to EU> [inv] 324 * 325 * where ET == element type and EU == element unit. 326 */ 327 if (argc < 2) { 328 warnx("%s: too few arguments", cname); 329 goto usage; 330 } else if (argc > 3) { 331 warnx("%s: too many arguments", cname); 332 goto usage; 333 } 334 bzero(&cmd, sizeof(cmd)); 335 336 /* <to ET> */ 337 cmd.cp_type = parse_element_type(*argv); 338 ++argv; --argc; 339 340 /* <to EU> */ 341 cmd.cp_unit = parse_element_unit(*argv); 342 ++argv; --argc; 343 344 /* Deal with optional command modifier. */ 345 if (argc) { 346 val = parse_special(*argv); 347 switch (val) { 348 case SW_INVERT: 349 cmd.cp_flags |= CP_INVERT; 350 break; 351 352 default: 353 errx(1, "%s: inappropriate modifier `%s'", 354 cname, *argv); 355 /* NOTREACHED */ 356 } 357 } 358 359 /* Send command to changer. */ 360 if (ioctl(changer_fd, CHIOPOSITION, (char *)&cmd)) 361 err(1, "%s: CHIOPOSITION", changer_name); 362 363 return (0); 364 365 usage: 366 fprintf(stderr, "usage: %s %s <to ET> <to EU> [inv]\n", 367 __progname, cname); 368 return (1); 369 } 370 371 static int 372 do_params(cname, argc, argv) 373 char *cname; 374 int argc; 375 char **argv; 376 { 377 struct changer_params data; 378 379 /* No arguments to this command. */ 380 if (argc) { 381 warnx("%s: no arguements expected", cname); 382 goto usage; 383 } 384 385 /* Get params from changer and display them. */ 386 bzero(&data, sizeof(data)); 387 if (ioctl(changer_fd, CHIOGPARAMS, (char *)&data)) 388 err(1, "%s: CHIOGPARAMS", changer_name); 389 390 printf("%s: %d slot%s, %d drive%s, %d picker%s", 391 changer_name, 392 data.cp_nslots, (data.cp_nslots > 1) ? "s" : "", 393 data.cp_ndrives, (data.cp_ndrives > 1) ? "s" : "", 394 data.cp_npickers, (data.cp_npickers > 1) ? "s" : ""); 395 if (data.cp_nportals) 396 printf(", %d portal%s", data.cp_nportals, 397 (data.cp_nportals > 1) ? "s" : ""); 398 printf("\n%s: current picker: %d\n", changer_name, data.cp_curpicker); 399 400 return (0); 401 402 usage: 403 fprintf(stderr, "usage: %s %s\n", __progname, cname); 404 return (1); 405 } 406 407 static int 408 do_getpicker(cname, argc, argv) 409 char *cname; 410 int argc; 411 char **argv; 412 { 413 int picker; 414 415 /* No arguments to this command. */ 416 if (argc) { 417 warnx("%s: no arguments expected", cname); 418 goto usage; 419 } 420 421 /* Get current picker from changer and display it. */ 422 if (ioctl(changer_fd, CHIOGPICKER, (char *)&picker)) 423 err(1, "%s: CHIOGPICKER", changer_name); 424 425 printf("%s: current picker: %d\n", changer_name, picker); 426 427 return (0); 428 429 usage: 430 fprintf(stderr, "usage: %s %s\n", __progname, cname); 431 return (1); 432 } 433 434 static int 435 do_setpicker(cname, argc, argv) 436 char *cname; 437 int argc; 438 char **argv; 439 { 440 int picker; 441 442 if (argc < 1) { 443 warnx("%s: too few arguments", cname); 444 goto usage; 445 } else if (argc > 1) { 446 warnx("%s: too many arguments", cname); 447 goto usage; 448 } 449 450 picker = parse_element_unit(*argv); 451 452 /* Set the changer picker. */ 453 if (ioctl(changer_fd, CHIOSPICKER, (char *)&picker)) 454 err(1, "%s: CHIOSPICKER", changer_name); 455 456 return (0); 457 458 usage: 459 fprintf(stderr, "usage: %s %s <picker>\n", __progname, cname); 460 return (1); 461 } 462 463 static int 464 do_status(cname, argc, argv) 465 char *cname; 466 int argc; 467 char **argv; 468 { 469 struct changer_element_status cmd; 470 struct changer_params data; 471 u_int8_t *statusp; 472 int i, count, chet, schet, echet; 473 char *cmdname, *description; 474 475 /* 476 * On a status command, we expect the following: 477 * 478 * [<ET>] 479 * 480 * where ET == element type. 481 * 482 * If we get no arguments, we get the status of all 483 * known element types. 484 */ 485 if (argc > 1) { 486 warnx("%s: too many arguments", cname); 487 goto usage; 488 } 489 490 /* 491 * Get params from changer. Specifically, we need the element 492 * counts. 493 */ 494 bzero(&data, sizeof(data)); 495 if (ioctl(changer_fd, CHIOGPARAMS, (char *)&data)) 496 err(1, "%s: CHIOGPARAMS", changer_name); 497 498 if (argc) 499 schet = echet = parse_element_type(*argv); 500 else { 501 schet = CHET_MT; 502 echet = CHET_DT; 503 } 504 505 for (chet = schet; chet <= echet; ++chet) { 506 switch (chet) { 507 case CHET_MT: 508 count = data.cp_npickers; 509 description = "picker"; 510 break; 511 512 case CHET_ST: 513 count = data.cp_nslots; 514 description = "slot"; 515 break; 516 517 case CHET_IE: 518 count = data.cp_nportals; 519 description = "portal"; 520 break; 521 522 case CHET_DT: 523 count = data.cp_ndrives; 524 description = "drive"; 525 break; 526 } 527 528 if (count == 0) { 529 if (argc == 0) 530 continue; 531 else { 532 printf("%s: no %s elements\n", 533 changer_name, description); 534 return (0); 535 } 536 } 537 538 /* Allocate storage for the status bytes. */ 539 if ((statusp = (u_int8_t *)malloc(count)) == NULL) 540 errx(1, "can't allocate status storage"); 541 542 bzero(statusp, count); 543 bzero(&cmd, sizeof(cmd)); 544 545 cmd.ces_type = chet; 546 cmd.ces_data = statusp; 547 548 if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cmd)) { 549 free(statusp); 550 err(1, "%s: CHIOGSTATUS", changer_name); 551 } 552 553 /* Dump the status for each element of this type. */ 554 for (i = 0; i < count; ++i) { 555 printf("%s %d: %s\n", description, i, 556 bits_to_string(statusp[i], CESTATUS_BITS)); 557 } 558 559 free(statusp); 560 } 561 562 return (0); 563 564 usage: 565 fprintf(stderr, "usage: %s %s [<element type>]\n", __progname, 566 cname); 567 return (1); 568 } 569 570 static int 571 parse_element_type(cp) 572 char *cp; 573 { 574 int i; 575 576 for (i = 0; elements[i].et_name != NULL; ++i) 577 if (strcmp(elements[i].et_name, cp) == 0) 578 return (elements[i].et_type); 579 580 errx(1, "invalid element type `%s'", cp); 581 } 582 583 static int 584 parse_element_unit(cp) 585 char *cp; 586 { 587 int i; 588 char *p; 589 590 i = (int)strtol(cp, &p, 10); 591 if ((i < 0) || (*p != '\0')) 592 errx(1, "invalid unit number `%s'", cp); 593 594 return (i); 595 } 596 597 static int 598 parse_special(cp) 599 char *cp; 600 { 601 int val; 602 603 val = is_special(cp); 604 if (val) 605 return (val); 606 607 errx(1, "invalid modifier `%s'", cp); 608 } 609 610 static int 611 is_special(cp) 612 char *cp; 613 { 614 int i; 615 616 for (i = 0; specials[i].sw_name != NULL; ++i) 617 if (strcmp(specials[i].sw_name, cp) == 0) 618 return (specials[i].sw_value); 619 620 return (0); 621 } 622 623 static char * 624 bits_to_string(v, cp) 625 int v; 626 const char *cp; 627 { 628 const char *np; 629 char f, sep, *bp; 630 static char buf[128]; 631 632 bp = buf; 633 bzero(buf, sizeof(buf)); 634 635 for (sep = '<'; (f = *cp++) != 0; cp = np) { 636 for (np = cp; *np >= ' ';) 637 np++; 638 if ((v & (1 << (f - 1))) == 0) 639 continue; 640 bp += sprintf(bp, "%c%.*s", sep, np - cp, cp); 641 sep = ','; 642 } 643 if (sep != '<') 644 *bp = '>'; 645 646 return (buf); 647 } 648 649 static void 650 cleanup() 651 { 652 653 /* Simple enough... */ 654 (void)close(changer_fd); 655 } 656 657 static void 658 usage() 659 { 660 661 fprintf(stderr, "usage: %s command arg1 arg2 ...\n", __progname); 662 exit(1); 663 } 664