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