| File: | src/bin/chio/chio.c |
| Warning: | line 767, column 5 Value stored to 'found' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* $OpenBSD: chio.c,v 1.29 2021/10/24 21:24:21 deraadt Exp $ */ |
| 2 | /* $NetBSD: chio.c,v 1.1.1.1 1996/04/03 00:34:38 thorpej Exp $ */ |
| 3 | |
| 4 | /* |
| 5 | * Copyright (c) 1996 Jason R. Thorpe <thorpej@and.com> |
| 6 | * All rights reserved. |
| 7 | * |
| 8 | * Redistribution and use in source and binary forms, with or without |
| 9 | * modification, are permitted provided that the following conditions |
| 10 | * are met: |
| 11 | * 1. Redistributions of source code must retain the above copyright |
| 12 | * notice, this list of conditions and the following disclaimer. |
| 13 | * 2. Redistributions in binary form must reproduce the above copyright |
| 14 | * notice, this list of conditions and the following disclaimer in the |
| 15 | * documentation and/or other materials provided with the distribution. |
| 16 | * 3. All advertising materials mentioning features or use of this software |
| 17 | * must display the following acknowledgments: |
| 18 | * This product includes software developed by Jason R. Thorpe |
| 19 | * for And Communications, http://www.and.com/ |
| 20 | * 4. The name of the author may not be used to endorse or promote products |
| 21 | * derived from this software without specific prior written permission. |
| 22 | * |
| 23 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| 24 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| 25 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| 26 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
| 27 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
| 28 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| 29 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
| 30 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| 31 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| 32 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| 33 | * SUCH DAMAGE. |
| 34 | */ |
| 35 | |
| 36 | #include <sys/types.h> |
| 37 | #include <sys/ioctl.h> |
| 38 | #include <sys/mtio.h> |
| 39 | #include <sys/chio.h> |
| 40 | #include <err.h> |
| 41 | #include <errno(*__errno()).h> |
| 42 | #include <fcntl.h> |
| 43 | #include <limits.h> |
| 44 | #include <stdio.h> |
| 45 | #include <stdlib.h> |
| 46 | #include <string.h> |
| 47 | #include <unistd.h> |
| 48 | #include <util.h> |
| 49 | |
| 50 | #include "defs.h" |
| 51 | #include "pathnames.h" |
| 52 | |
| 53 | #define _PATH_CH_CONF"/etc/chio.conf" "/etc/chio.conf" |
| 54 | extern char *parse_tapedev(const char *, const char *, int); /* parse.y */ |
| 55 | extern char *__progname; /* from crt0.o */ |
| 56 | |
| 57 | static void usage(void); |
| 58 | static int parse_element_type(char *); |
| 59 | static int parse_element_unit(char *); |
| 60 | static int parse_special(char *); |
| 61 | static int is_special(char *); |
| 62 | static const char * element_type_name(int et); |
| 63 | static char *bits_to_string(int, const char *); |
| 64 | static void find_voltag(char *, int *, int *); |
| 65 | static void check_source_drive(int); |
| 66 | |
| 67 | static int do_move(char *, int, char **); |
| 68 | static int do_exchange(char *, int, char **); |
| 69 | static int do_position(char *, int, char **); |
| 70 | static int do_params(char *, int, char **); |
| 71 | static int do_getpicker(char *, int, char **); |
| 72 | static int do_setpicker(char *, int, char **); |
| 73 | static int do_status(char *, int, char **); |
| 74 | |
| 75 | /* Valid changer element types. */ |
| 76 | const struct element_type elements[] = { |
| 77 | { "drive", CHET_DT3 }, |
| 78 | { "picker", CHET_MT0 }, |
| 79 | { "portal", CHET_IE2 }, |
| 80 | { "slot", CHET_ST1 }, |
| 81 | { NULL((void *)0), 0 }, |
| 82 | }; |
| 83 | |
| 84 | /* Valid commands. */ |
| 85 | const struct changer_command commands[] = { |
| 86 | { "exchange", do_exchange }, |
| 87 | { "getpicker", do_getpicker }, |
| 88 | { "move", do_move }, |
| 89 | { "params", do_params }, |
| 90 | { "position", do_position }, |
| 91 | { "setpicker", do_setpicker }, |
| 92 | { "status", do_status }, |
| 93 | { NULL((void *)0), 0 }, |
| 94 | }; |
| 95 | |
| 96 | /* Valid special words. */ |
| 97 | const struct special_word specials[] = { |
| 98 | { "inv", SW_INVERT1 }, |
| 99 | { "inv1", SW_INVERT12 }, |
| 100 | { "inv2", SW_INVERT23 }, |
| 101 | { NULL((void *)0), 0 }, |
| 102 | }; |
| 103 | |
| 104 | static int changer_fd; |
| 105 | static char *changer_name; |
| 106 | static int avoltag; |
| 107 | static int pvoltag; |
| 108 | static int sense; |
| 109 | static int source; |
| 110 | |
| 111 | int |
| 112 | main(int argc, char *argv[]) |
| 113 | { |
| 114 | int ch, i; |
| 115 | |
| 116 | while ((ch = getopt(argc, argv, "f:")) != -1) { |
| 117 | switch (ch) { |
| 118 | case 'f': |
| 119 | changer_name = optarg; |
| 120 | break; |
| 121 | default: |
| 122 | usage(); |
| 123 | } |
| 124 | } |
| 125 | argc -= optind; |
| 126 | argv += optind; |
| 127 | |
| 128 | if (argc == 0) |
| 129 | usage(); |
| 130 | |
| 131 | /* Get the default changer if not already specified. */ |
| 132 | if (changer_name == NULL((void *)0)) |
| 133 | if ((changer_name = getenv(CHANGER_ENV_VAR"CHANGER")) == NULL((void *)0)) |
| 134 | changer_name = _PATH_CH"/dev/ch0"; |
| 135 | |
| 136 | /* Open the changer device. */ |
| 137 | if ((changer_fd = open(changer_name, O_RDWR0x0002)) == -1) |
| 138 | err(1, "%s: open", changer_name); |
| 139 | |
| 140 | /* Find the specified command. */ |
| 141 | for (i = 0; commands[i].cc_name != NULL((void *)0); ++i) |
| 142 | if (strcmp(*argv, commands[i].cc_name) == 0) |
| 143 | break; |
| 144 | if (commands[i].cc_name == NULL((void *)0)) { |
| 145 | /* look for abbreviation */ |
| 146 | for (i = 0; commands[i].cc_name != NULL((void *)0); ++i) |
| 147 | if (strncmp(*argv, commands[i].cc_name, |
| 148 | strlen(*argv)) == 0) |
| 149 | break; |
| 150 | } |
| 151 | if (commands[i].cc_name == NULL((void *)0)) |
| 152 | errx(1, "unknown command: %s", *argv); |
| 153 | |
| 154 | exit((*commands[i].cc_handler)(commands[i].cc_name, argc, argv)); |
| 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 | bzero(&cmd, sizeof(cmd)); |
| 181 | |
| 182 | /* |
| 183 | * Get the from ET and EU - we search for it if the ET is |
| 184 | * "voltag", otherwise, we just use the ET and EU given to us. |
| 185 | */ |
| 186 | if (strcmp(*argv, "voltag") == 0) { |
| 187 | ++argv; --argc; |
| 188 | find_voltag(*argv, &cmd.cm_fromtype, &cmd.cm_fromunit); |
| 189 | ++argv; --argc; |
| 190 | } else { |
| 191 | cmd.cm_fromtype = parse_element_type(*argv); |
| 192 | ++argv; --argc; |
| 193 | cmd.cm_fromunit = parse_element_unit(*argv); |
| 194 | ++argv; --argc; |
| 195 | } |
| 196 | |
| 197 | if (cmd.cm_fromtype == CHET_DT3) |
| 198 | check_source_drive(cmd.cm_fromunit); |
| 199 | |
| 200 | /* |
| 201 | * Don't allow voltag on the to ET, using a volume |
| 202 | * as a destination makes no sense on a move |
| 203 | */ |
| 204 | cmd.cm_totype = parse_element_type(*argv); |
| 205 | ++argv; --argc; |
| 206 | cmd.cm_tounit = parse_element_unit(*argv); |
| 207 | ++argv; --argc; |
| 208 | |
| 209 | /* Deal with optional command modifier. */ |
| 210 | if (argc) { |
| 211 | val = parse_special(*argv); |
| 212 | switch (val) { |
| 213 | case SW_INVERT1: |
| 214 | cmd.cm_flags |= CM_INVERT0x01; |
| 215 | break; |
| 216 | |
| 217 | default: |
| 218 | errx(1, "%s: inappropriate modifier `%s'", |
| 219 | cname, *argv); |
| 220 | /* NOTREACHED */ |
| 221 | } |
| 222 | } |
| 223 | |
| 224 | /* Send command to changer. */ |
| 225 | if (ioctl(changer_fd, CHIOMOVE((unsigned long)0x80000000 | ((sizeof(struct changer_move) & 0x1fff) << 16) | ((('c')) << 8) | ((0x41))), &cmd) == -1) |
| 226 | err(1, "%s: CHIOMOVE", changer_name); |
| 227 | |
| 228 | return (0); |
| 229 | |
| 230 | usage: |
| 231 | fprintf(stderr(&__sF[2]), "usage: %s %s " |
| 232 | "<from ET> <from EU> <to ET> <to EU> [inv]\n", __progname, cname); |
| 233 | return (1); |
| 234 | } |
| 235 | |
| 236 | static int |
| 237 | do_exchange(char *cname, int argc, char *argv[]) |
| 238 | { |
| 239 | struct changer_exchange cmd; |
| 240 | int val; |
| 241 | |
| 242 | /* |
| 243 | * On an exchange command, we expect the following: |
| 244 | * |
| 245 | * <src ET> <src EU> <dst1 ET> <dst1 EU> [<dst2 ET> <dst2 EU>] [inv1] [inv2] |
| 246 | * |
| 247 | * where ET == element type and EU == element unit. |
| 248 | */ |
| 249 | |
| 250 | ++argv; --argc; |
| 251 | |
| 252 | if (argc < 4) { |
| 253 | warnx("%s: too few arguments", cname); |
| 254 | goto usage; |
| 255 | } else if (argc > 8) { |
| 256 | warnx("%s: too many arguments", cname); |
| 257 | goto usage; |
| 258 | } |
| 259 | bzero(&cmd, sizeof(cmd)); |
| 260 | |
| 261 | /* <src ET> */ |
| 262 | cmd.ce_srctype = parse_element_type(*argv); |
| 263 | ++argv; --argc; |
| 264 | |
| 265 | /* <src EU> */ |
| 266 | cmd.ce_srcunit = parse_element_unit(*argv); |
| 267 | ++argv; --argc; |
| 268 | |
| 269 | /* <dst1 ET> */ |
| 270 | cmd.ce_fdsttype = parse_element_type(*argv); |
| 271 | ++argv; --argc; |
| 272 | |
| 273 | /* <dst1 EU> */ |
| 274 | cmd.ce_fdstunit = parse_element_unit(*argv); |
| 275 | ++argv; --argc; |
| 276 | |
| 277 | /* |
| 278 | * If the next token is a special word or there are no more |
| 279 | * arguments, then this is a case of simple exchange. |
| 280 | * dst2 == src. |
| 281 | */ |
| 282 | if ((argc == 0) || is_special(*argv)) { |
| 283 | cmd.ce_sdsttype = cmd.ce_srctype; |
| 284 | cmd.ce_sdstunit = cmd.ce_srcunit; |
| 285 | goto do_special; |
| 286 | } |
| 287 | |
| 288 | /* <dst2 ET> */ |
| 289 | cmd.ce_sdsttype = parse_element_type(*argv); |
| 290 | ++argv; --argc; |
| 291 | |
| 292 | /* <dst2 EU> */ |
| 293 | cmd.ce_sdstunit = parse_element_unit(*argv); |
| 294 | ++argv; --argc; |
| 295 | |
| 296 | do_special: |
| 297 | /* Deal with optional command modifiers. */ |
| 298 | while (argc) { |
| 299 | val = parse_special(*argv); |
| 300 | ++argv; --argc; |
| 301 | switch (val) { |
| 302 | case SW_INVERT12: |
| 303 | cmd.ce_flags |= CE_INVERT10x01; |
| 304 | break; |
| 305 | |
| 306 | case SW_INVERT23: |
| 307 | cmd.ce_flags |= CE_INVERT20x02; |
| 308 | break; |
| 309 | |
| 310 | default: |
| 311 | errx(1, "%s: inappropriate modifier `%s'", |
| 312 | cname, *argv); |
| 313 | /* NOTREACHED */ |
| 314 | } |
| 315 | } |
| 316 | |
| 317 | /* Send command to changer. */ |
| 318 | if (ioctl(changer_fd, CHIOEXCHANGE((unsigned long)0x80000000 | ((sizeof(struct changer_exchange ) & 0x1fff) << 16) | ((('c')) << 8) | ((0x42) )), &cmd) == -1) |
| 319 | err(1, "%s: CHIOEXCHANGE", changer_name); |
| 320 | |
| 321 | return (0); |
| 322 | |
| 323 | usage: |
| 324 | fprintf(stderr(&__sF[2]), "usage: %s %s <src ET> <src EU> <dst1 ET> <dst1 EU>\n" |
| 325 | " [<dst2 ET> <dst2 EU>] [inv1] [inv2]\n", |
| 326 | __progname, cname); |
| 327 | return (1); |
| 328 | } |
| 329 | |
| 330 | static int |
| 331 | do_position(char *cname, int argc, char *argv[]) |
| 332 | { |
| 333 | struct changer_position cmd; |
| 334 | int val; |
| 335 | |
| 336 | /* |
| 337 | * On a position command, we expect the following: |
| 338 | * |
| 339 | * <to ET> <to EU> [inv] |
| 340 | * |
| 341 | * where ET == element type and EU == element unit. |
| 342 | */ |
| 343 | |
| 344 | ++argv; --argc; |
| 345 | |
| 346 | if (argc < 2) { |
| 347 | warnx("%s: too few arguments", cname); |
| 348 | goto usage; |
| 349 | } else if (argc > 3) { |
| 350 | warnx("%s: too many arguments", cname); |
| 351 | goto usage; |
| 352 | } |
| 353 | bzero(&cmd, sizeof(cmd)); |
| 354 | |
| 355 | /* <to ET> */ |
| 356 | cmd.cp_type = parse_element_type(*argv); |
| 357 | ++argv; --argc; |
| 358 | |
| 359 | /* <to EU> */ |
| 360 | cmd.cp_unit = parse_element_unit(*argv); |
| 361 | ++argv; --argc; |
| 362 | |
| 363 | /* Deal with optional command modifier. */ |
| 364 | if (argc) { |
| 365 | val = parse_special(*argv); |
| 366 | switch (val) { |
| 367 | case SW_INVERT1: |
| 368 | cmd.cp_flags |= CP_INVERT0x01; |
| 369 | break; |
| 370 | |
| 371 | default: |
| 372 | errx(1, "%s: inappropriate modifier `%s'", |
| 373 | cname, *argv); |
| 374 | /* NOTREACHED */ |
| 375 | } |
| 376 | } |
| 377 | |
| 378 | /* Send command to changer. */ |
| 379 | if (ioctl(changer_fd, CHIOPOSITION((unsigned long)0x80000000 | ((sizeof(struct changer_position ) & 0x1fff) << 16) | ((('c')) << 8) | ((0x43) )), &cmd) == -1) |
| 380 | err(1, "%s: CHIOPOSITION", changer_name); |
| 381 | |
| 382 | return (0); |
| 383 | |
| 384 | usage: |
| 385 | fprintf(stderr(&__sF[2]), "usage: %s %s <to ET> <to EU> [inv]\n", |
| 386 | __progname, cname); |
| 387 | return (1); |
| 388 | } |
| 389 | |
| 390 | static int |
| 391 | do_params(char *cname, int argc, char *argv[]) |
| 392 | { |
| 393 | struct changer_params data; |
| 394 | |
| 395 | /* No arguments to this command. */ |
| 396 | |
| 397 | ++argv; --argc; |
| 398 | |
| 399 | if (argc) { |
| 400 | warnx("%s: no arguments expected", cname); |
| 401 | goto usage; |
| 402 | } |
| 403 | |
| 404 | /* Get params from changer and display them. */ |
| 405 | bzero(&data, sizeof(data)); |
| 406 | if (ioctl(changer_fd, CHIOGPARAMS((unsigned long)0x40000000 | ((sizeof(struct changer_params) & 0x1fff) << 16) | ((('c')) << 8) | ((0x46))), &data) == -1) |
| 407 | err(1, "%s: CHIOGPARAMS", changer_name); |
| 408 | |
| 409 | printf("%s: %d slot%s, %d drive%s, %d picker%s", |
| 410 | changer_name, |
| 411 | data.cp_nslots, (data.cp_nslots > 1) ? "s" : "", |
| 412 | data.cp_ndrives, (data.cp_ndrives > 1) ? "s" : "", |
| 413 | data.cp_npickers, (data.cp_npickers > 1) ? "s" : ""); |
| 414 | if (data.cp_nportals) |
| 415 | printf(", %d portal%s", data.cp_nportals, |
| 416 | (data.cp_nportals > 1) ? "s" : ""); |
| 417 | printf("\n%s: current picker: %d\n", changer_name, data.cp_curpicker); |
| 418 | |
| 419 | return (0); |
| 420 | |
| 421 | usage: |
| 422 | fprintf(stderr(&__sF[2]), "usage: %s %s\n", __progname, cname); |
| 423 | return (1); |
| 424 | } |
| 425 | |
| 426 | static int |
| 427 | do_getpicker(char *cname, int argc, char *argv[]) |
| 428 | { |
| 429 | int picker; |
| 430 | |
| 431 | /* No arguments to this command. */ |
| 432 | |
| 433 | ++argv; --argc; |
| 434 | |
| 435 | if (argc) { |
| 436 | warnx("%s: no arguments expected", cname); |
| 437 | goto usage; |
| 438 | } |
| 439 | |
| 440 | /* Get current picker from changer and display it. */ |
| 441 | if (ioctl(changer_fd, CHIOGPICKER((unsigned long)0x40000000 | ((sizeof(int) & 0x1fff) << 16) | ((('c')) << 8) | ((0x44))), &picker) == -1) |
| 442 | err(1, "%s: CHIOGPICKER", changer_name); |
| 443 | |
| 444 | printf("%s: current picker: %d\n", changer_name, picker); |
| 445 | |
| 446 | return (0); |
| 447 | |
| 448 | usage: |
| 449 | fprintf(stderr(&__sF[2]), "usage: %s %s\n", __progname, cname); |
| 450 | return (1); |
| 451 | } |
| 452 | |
| 453 | static int |
| 454 | do_setpicker(char *cname, int argc, char *argv[]) |
| 455 | { |
| 456 | int picker; |
| 457 | |
| 458 | ++argv; --argc; |
| 459 | |
| 460 | if (argc < 1) { |
| 461 | warnx("%s: too few arguments", cname); |
| 462 | goto usage; |
| 463 | } else if (argc > 1) { |
| 464 | warnx("%s: too many arguments", cname); |
| 465 | goto usage; |
| 466 | } |
| 467 | |
| 468 | picker = parse_element_unit(*argv); |
| 469 | |
| 470 | /* Set the changer picker. */ |
| 471 | if (ioctl(changer_fd, CHIOSPICKER((unsigned long)0x80000000 | ((sizeof(int) & 0x1fff) << 16) | ((('c')) << 8) | ((0x45))), &picker) == -1) |
| 472 | err(1, "%s: CHIOSPICKER", changer_name); |
| 473 | |
| 474 | return (0); |
| 475 | |
| 476 | usage: |
| 477 | fprintf(stderr(&__sF[2]), "usage: %s %s <picker>\n", __progname, cname); |
| 478 | return (1); |
| 479 | } |
| 480 | |
| 481 | static int |
| 482 | do_status(char *cname, int argc, char *argv[]) |
| 483 | { |
| 484 | struct changer_element_status_request cmd; |
| 485 | struct changer_params data; |
| 486 | int i, chet, schet, echet, c; |
| 487 | char *description; |
| 488 | size_t count; |
| 489 | |
| 490 | optreset = 1; |
| 491 | optind = 1; |
| 492 | while ((c = getopt(argc, argv, "SsvVa")) != -1) { |
| 493 | switch (c) { |
| 494 | case 's': |
| 495 | sense = 1; |
| 496 | break; |
| 497 | case 'S': |
| 498 | source = 1; |
| 499 | break; |
| 500 | case 'v': |
| 501 | pvoltag = 1; |
| 502 | break; |
| 503 | case 'V': |
| 504 | avoltag = 1; |
| 505 | break; |
| 506 | case 'a': |
| 507 | pvoltag = avoltag = source = sense = 1; |
| 508 | break; |
| 509 | default: |
| 510 | goto usage; |
| 511 | } |
| 512 | } |
| 513 | |
| 514 | argc -= optind; |
| 515 | argv += optind; |
| 516 | |
| 517 | /* |
| 518 | * On a status command, we expect the following: |
| 519 | * |
| 520 | * [<ET>] |
| 521 | * |
| 522 | * where ET == element type. |
| 523 | * |
| 524 | * If we get no arguments, we get the status of all |
| 525 | * known element types. |
| 526 | */ |
| 527 | if (argc > 1) { |
| 528 | warnx("%s: too many arguments", cname); |
| 529 | goto usage; |
| 530 | } |
| 531 | |
| 532 | /* |
| 533 | * Get params from changer. Specifically, we need the element |
| 534 | * counts. |
| 535 | */ |
| 536 | bzero(&data, sizeof(data)); |
| 537 | if (ioctl(changer_fd, CHIOGPARAMS((unsigned long)0x40000000 | ((sizeof(struct changer_params) & 0x1fff) << 16) | ((('c')) << 8) | ((0x46))), &data) == -1) |
| 538 | err(1, "%s: CHIOGPARAMS", changer_name); |
| 539 | |
| 540 | if (argc) |
| 541 | schet = echet = parse_element_type(*argv); |
| 542 | else { |
| 543 | schet = CHET_MT0; |
| 544 | echet = CHET_DT3; |
| 545 | } |
| 546 | |
| 547 | for (chet = schet; chet <= echet; ++chet) { |
| 548 | switch (chet) { |
| 549 | case CHET_MT0: |
| 550 | count = data.cp_npickers; |
| 551 | description = "picker"; |
| 552 | break; |
| 553 | |
| 554 | case CHET_ST1: |
| 555 | count = data.cp_nslots; |
| 556 | description = "slot"; |
| 557 | break; |
| 558 | |
| 559 | case CHET_IE2: |
| 560 | count = data.cp_nportals; |
| 561 | description = "portal"; |
| 562 | break; |
| 563 | |
| 564 | case CHET_DT3: |
| 565 | count = data.cp_ndrives; |
| 566 | description = "drive"; |
| 567 | break; |
| 568 | } |
| 569 | |
| 570 | if (count == 0) { |
| 571 | if (argc == 0) |
| 572 | continue; |
| 573 | else { |
| 574 | printf("%s: no %s elements\n", |
| 575 | changer_name, description); |
| 576 | return (0); |
| 577 | } |
| 578 | } |
| 579 | |
| 580 | bzero(&cmd, sizeof(cmd)); |
| 581 | |
| 582 | cmd.cesr_type = chet; |
| 583 | /* Allocate storage for the status info. */ |
| 584 | cmd.cesr_data = calloc(count, sizeof(*cmd.cesr_data)); |
| 585 | if ((cmd.cesr_data) == NULL((void *)0)) |
| 586 | errx(1, "can't allocate status storage"); |
| 587 | if (avoltag || pvoltag) |
| 588 | cmd.cesr_flags |= CESR_VOLTAGS0x01; |
| 589 | |
| 590 | if (ioctl(changer_fd, CHIOGSTATUS((unsigned long)0x80000000 | ((sizeof(struct changer_element_status_request ) & 0x1fff) << 16) | ((('c')) << 8) | ((0x48) )), &cmd) == -1) { |
| 591 | free(cmd.cesr_data); |
| 592 | err(1, "%s: CHIOGSTATUS", changer_name); |
| 593 | } |
| 594 | |
| 595 | /* Dump the status for each element of this type. */ |
| 596 | for (i = 0; i < count; ++i) { |
| 597 | struct changer_element_status *ces = |
| 598 | &(cmd.cesr_data[i]); |
| 599 | printf("%s %d: %s", description, i, |
| 600 | bits_to_string(ces->ces_flags, CESTATUS_BITS"\20\6INEAB\5EXENAB\4ACCESS\3EXCEPT\2IMPEXP\1FULL")); |
| 601 | if (sense) |
| 602 | printf(" sense: <0x%02x/0x%02x>", |
| 603 | ces->ces_sensecode, |
| 604 | ces->ces_sensequal); |
| 605 | if (pvoltag) |
| 606 | printf(" voltag: <%s:%d>", |
| 607 | ces->ces_pvoltag.cv_volid, |
| 608 | ces->ces_pvoltag.cv_serial); |
| 609 | if (avoltag) |
| 610 | printf(" avoltag: <%s:%d>", |
| 611 | ces->ces_avoltag.cv_volid, |
| 612 | ces->ces_avoltag.cv_serial); |
| 613 | if (source) { |
| 614 | if (ces->ces_flags & CESTATUS_ACCESS0x08) |
| 615 | printf(" source: <%s %d>", |
| 616 | element_type_name( |
| 617 | ces->ces_source_type), |
| 618 | ces->ces_source_addr); |
| 619 | else |
| 620 | printf(" source: <>"); |
| 621 | } |
| 622 | printf("\n"); |
| 623 | } |
| 624 | |
| 625 | free(cmd.cesr_data); |
| 626 | } |
| 627 | |
| 628 | return (0); |
| 629 | |
| 630 | usage: |
| 631 | fprintf(stderr(&__sF[2]), "usage: %s %s [<element type>]\n", __progname, |
| 632 | cname); |
| 633 | return (1); |
| 634 | } |
| 635 | |
| 636 | /* |
| 637 | * Check a drive unit as the source for a move or exchange |
| 638 | * operation. If the drive is not accessible, we attempt |
| 639 | * to unmount the tape in it before moving to avoid |
| 640 | * errors in "disconnected" type pickers where the drive |
| 641 | * is on a separate target from the changer. |
| 642 | */ |
| 643 | static void |
| 644 | check_source_drive(int unit) |
| 645 | { |
| 646 | struct mtop mtoffl = { MTOFFL6, 1 }; |
| 647 | struct changer_element_status_request cmd; |
| 648 | struct changer_element_status *ces; |
| 649 | struct changer_params data; |
| 650 | size_t count = 0; |
| 651 | int mtfd; |
| 652 | char *tapedev; |
| 653 | |
| 654 | /* |
| 655 | * Get params from changer. Specifically, we need the element |
| 656 | * counts. |
| 657 | */ |
| 658 | bzero(&data, sizeof(data)); |
| 659 | if (ioctl(changer_fd, CHIOGPARAMS((unsigned long)0x40000000 | ((sizeof(struct changer_params) & 0x1fff) << 16) | ((('c')) << 8) | ((0x46))), &data) == -1) |
| 660 | err(1, "%s: CHIOGPARAMS", changer_name); |
| 661 | |
| 662 | count = data.cp_ndrives; |
| 663 | if (unit < 0 || unit >= count) |
| 664 | err(1, "%s: invalid drive: drive %d", changer_name, unit); |
| 665 | |
| 666 | bzero(&cmd, sizeof(cmd)); |
| 667 | cmd.cesr_type = CHET_DT3; |
| 668 | /* Allocate storage for the status info. */ |
| 669 | cmd.cesr_data = calloc(count, sizeof(*cmd.cesr_data)); |
| 670 | if ((cmd.cesr_data) == NULL((void *)0)) |
| 671 | errx(1, "can't allocate status storage"); |
| 672 | |
| 673 | if (ioctl(changer_fd, CHIOGSTATUS((unsigned long)0x80000000 | ((sizeof(struct changer_element_status_request ) & 0x1fff) << 16) | ((('c')) << 8) | ((0x48) )), &cmd) == -1) { |
| 674 | free(cmd.cesr_data); |
| 675 | err(1, "%s: CHIOGSTATUS", changer_name); |
| 676 | } |
| 677 | ces = &(cmd.cesr_data[unit]); |
| 678 | |
| 679 | if ((ces->ces_flags & CESTATUS_FULL0x01) != CESTATUS_FULL0x01) |
| 680 | err(1, "%s: drive %d is empty!", changer_name, unit); |
| 681 | |
| 682 | if ((ces->ces_flags & CESTATUS_ACCESS0x08) == CESTATUS_ACCESS0x08) |
| 683 | return; /* changer thinks all is well - trust it */ |
| 684 | |
| 685 | /* |
| 686 | * Otherwise, drive is FULL, but not accessible. |
| 687 | * Try to make it accessible by doing an mt offline. |
| 688 | */ |
| 689 | tapedev = parse_tapedev(_PATH_CH_CONF"/etc/chio.conf", changer_name, unit); |
| 690 | mtfd = opendev(tapedev, O_RDONLY0x0000, 0, NULL((void *)0)); |
| 691 | if (mtfd == -1) |
| 692 | err(1, "%s drive %d (%s): open", changer_name, unit, tapedev); |
| 693 | if (ioctl(mtfd, MTIOCTOP((unsigned long)0x80000000 | ((sizeof(struct mtop) & 0x1fff ) << 16) | ((('m')) << 8) | ((1))), &mtoffl) == -1) |
| 694 | err(1, "%s drive %d (%s): rewoffl", changer_name, unit, |
| 695 | tapedev); |
| 696 | close(mtfd); |
| 697 | } |
| 698 | |
| 699 | void |
| 700 | find_voltag(char *voltag, int *type, int *unit) |
| 701 | { |
| 702 | struct changer_element_status_request cmd; |
| 703 | struct changer_params data; |
| 704 | int i, chet, schet, echet, found; |
| 705 | size_t count = 0; |
| 706 | |
| 707 | /* |
| 708 | * Get params from changer. Specifically, we need the element |
| 709 | * counts. |
| 710 | */ |
| 711 | bzero(&data, sizeof(data)); |
| 712 | if (ioctl(changer_fd, CHIOGPARAMS((unsigned long)0x40000000 | ((sizeof(struct changer_params) & 0x1fff) << 16) | ((('c')) << 8) | ((0x46))), &data) == -1) |
| 713 | err(1, "%s: CHIOGPARAMS", changer_name); |
| 714 | |
| 715 | found = 0; |
| 716 | schet = CHET_MT0; |
| 717 | echet = CHET_DT3; |
| 718 | |
| 719 | /* |
| 720 | * For each type of element, iterate through each one until |
| 721 | * we find the correct volume id. |
| 722 | */ |
| 723 | for (chet = schet; chet <= echet; ++chet) { |
| 724 | switch (chet) { |
| 725 | case CHET_MT0: |
| 726 | count = data.cp_npickers; |
| 727 | break; |
| 728 | case CHET_ST1: |
| 729 | count = data.cp_nslots; |
| 730 | break; |
| 731 | case CHET_IE2: |
| 732 | count = data.cp_nportals; |
| 733 | break; |
| 734 | case CHET_DT3: |
| 735 | count = data.cp_ndrives; |
| 736 | break; |
| 737 | } |
| 738 | if (count == 0 || found) |
| 739 | continue; |
| 740 | |
| 741 | bzero(&cmd, sizeof(cmd)); |
| 742 | cmd.cesr_type = chet; |
| 743 | /* Allocate storage for the status info. */ |
| 744 | cmd.cesr_data = calloc(count, sizeof(*cmd.cesr_data)); |
| 745 | if ((cmd.cesr_data) == NULL((void *)0)) |
| 746 | errx(1, "can't allocate status storage"); |
| 747 | cmd.cesr_flags |= CESR_VOLTAGS0x01; |
| 748 | |
| 749 | if (ioctl(changer_fd, CHIOGSTATUS((unsigned long)0x80000000 | ((sizeof(struct changer_element_status_request ) & 0x1fff) << 16) | ((('c')) << 8) | ((0x48) )), &cmd) == -1) { |
| 750 | free(cmd.cesr_data); |
| 751 | err(1, "%s: CHIOGSTATUS", changer_name); |
| 752 | } |
| 753 | |
| 754 | /* |
| 755 | * look through each element to see if it has our desired |
| 756 | * volume tag. |
| 757 | */ |
| 758 | for (i = 0; i < count; ++i) { |
| 759 | struct changer_element_status *ces = |
| 760 | &(cmd.cesr_data[i]); |
| 761 | if ((ces->ces_flags & CESTATUS_FULL0x01) != CESTATUS_FULL0x01) |
| 762 | continue; /* no tape in drive */ |
| 763 | if (strcasecmp(voltag, ces->ces_pvoltag.cv_volid) |
| 764 | == 0) { |
| 765 | *type = chet; |
| 766 | *unit = i; |
| 767 | found = 1; |
Value stored to 'found' is never read | |
| 768 | free(cmd.cesr_data); |
| 769 | return; |
| 770 | } |
| 771 | } |
| 772 | free(cmd.cesr_data); |
| 773 | } |
| 774 | errx(1, "%s: unable to locate voltag: %s", changer_name, voltag); |
| 775 | } |
| 776 | |
| 777 | |
| 778 | static int |
| 779 | parse_element_type(char *cp) |
| 780 | { |
| 781 | int i; |
| 782 | |
| 783 | for (i = 0; elements[i].et_name != NULL((void *)0); ++i) |
| 784 | if (strcmp(elements[i].et_name, cp) == 0) |
| 785 | return (elements[i].et_type); |
| 786 | |
| 787 | errx(1, "invalid element type `%s'", cp); |
| 788 | } |
| 789 | |
| 790 | static const char * |
| 791 | element_type_name(int et) |
| 792 | { |
| 793 | int i; |
| 794 | |
| 795 | for (i = 0; elements[i].et_name != NULL((void *)0); i++) |
| 796 | if (elements[i].et_type == et) |
| 797 | return elements[i].et_name; |
| 798 | |
| 799 | return "unknown"; |
| 800 | } |
| 801 | |
| 802 | static int |
| 803 | parse_element_unit(char *cp) |
| 804 | { |
| 805 | int i; |
| 806 | char *p; |
| 807 | |
| 808 | i = (int)strtol(cp, &p, 10); |
| 809 | if ((i < 0) || (*p != '\0')) |
| 810 | errx(1, "invalid unit number `%s'", cp); |
| 811 | |
| 812 | return (i); |
| 813 | } |
| 814 | |
| 815 | static int |
| 816 | parse_special(char *cp) |
| 817 | { |
| 818 | int val; |
| 819 | |
| 820 | val = is_special(cp); |
| 821 | if (val) |
| 822 | return (val); |
| 823 | |
| 824 | errx(1, "invalid modifier `%s'", cp); |
| 825 | } |
| 826 | |
| 827 | static int |
| 828 | is_special(char *cp) |
| 829 | { |
| 830 | int i; |
| 831 | |
| 832 | for (i = 0; specials[i].sw_name != NULL((void *)0); ++i) |
| 833 | if (strcmp(specials[i].sw_name, cp) == 0) |
| 834 | return (specials[i].sw_value); |
| 835 | |
| 836 | return (0); |
| 837 | } |
| 838 | |
| 839 | static char * |
| 840 | bits_to_string(int v, const char *cp) |
| 841 | { |
| 842 | const char *np; |
| 843 | char f, sep, *bp; |
| 844 | static char buf[128]; |
| 845 | |
| 846 | bp = buf; |
| 847 | bzero(buf, sizeof(buf)); |
| 848 | |
| 849 | for (sep = '<'; (f = *cp++) != 0; cp = np) { |
| 850 | for (np = cp; *np >= ' ';) |
| 851 | np++; |
| 852 | if ((v & (1 << (f - 1))) == 0) |
| 853 | continue; |
| 854 | (void)snprintf(bp, sizeof(buf) - (bp - &buf[0]), |
| 855 | "%c%.*s", sep, (int)(np - cp), cp); |
| 856 | bp += strlen(bp); |
| 857 | sep = ','; |
| 858 | } |
| 859 | if (sep != '<') |
| 860 | *bp = '>'; |
| 861 | |
| 862 | return (buf); |
| 863 | } |
| 864 | |
| 865 | static void |
| 866 | usage(void) |
| 867 | { |
| 868 | int i; |
| 869 | |
| 870 | fprintf(stderr(&__sF[2]), "usage: %s [-f changer] command [arg ...]\n", |
| 871 | __progname); |
| 872 | exit(1); |
| 873 | } |