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