| File: | src/bin/chio/chio.c |
| Warning: | line 570, column 13 The left operand of '==' is a garbage value |
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; | |||
| 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 | } |