Initial import from FreeBSD RELENG_4:
[dragonfly.git] / bin / chio / chio.c
1 /*      $NetBSD: chio.c,v 1.6 1998/01/04 23:53:58 thorpej Exp $ */
2 /*
3  * Copyright (c) 1996 Jason R. Thorpe <thorpej@and.com>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgements:
16  *      This product includes software developed by Jason R. Thorpe
17  *      for And Communications, http://www.and.com/
18  * 4. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 /*
34  * Additional Copyright (c) 1997, by Matthew Jacob, for NASA/Ames Research Ctr.
35  * Addidional Copyright (c) 2000, by C. Stephen Gunn, Waterspout Communications
36  */
37
38 #ifndef lint
39 static const char copyright[] =
40         "@(#) Copyright (c) 1996 Jason R. Thorpe.  All rights reserved.";
41 static const char rcsid[] =
42   "$FreeBSD: src/bin/chio/chio.c,v 1.15.2.3 2001/07/28 19:22:01 mikeh Exp $";
43 #endif /* not lint */
44
45 #include <sys/param.h>
46 #include <sys/chio.h> 
47 #include <err.h>
48 #include <fcntl.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <unistd.h>
53
54 #include "defs.h"
55 #include "pathnames.h"
56
57 extern  char *__progname;       /* from crt0.o */
58
59 static  void usage __P((void));
60 static  void cleanup __P((void));
61 static  int parse_element_type __P((char *));
62 static  int parse_element_unit __P((char *));
63 static  const char * element_type_name __P((int et));
64 static  int parse_special __P((char *));
65 static  int is_special __P((char *));
66 static  const char *bits_to_string __P((ces_status_flags, const char *));
67
68 static  void find_element __P((char *, u_int16_t *, u_int16_t *));
69 static  struct changer_element_status *get_element_status
70             __P((unsigned int, unsigned int));
71
72 static  int do_move __P((const char *, int, char **));
73 static  int do_exchange __P((const char *, int, char **));
74 static  int do_position __P((const char *, int, char **));
75 static  int do_params __P((const char *, int, char **));
76 static  int do_getpicker __P((const char *, int, char **));
77 static  int do_setpicker __P((const char *, int, char **));
78 static  int do_status __P((const char *, int, char **));
79 static  int do_ielem __P((const char *, int, char **));
80 static  int do_return __P((const char *, int, char **));
81 static  int do_voltag __P((const char *, int, char **));
82
83 #ifndef CHET_VT
84 #define CHET_VT         10                      /* Completely Arbitrary */
85 #endif
86
87 /* Valid changer element types. */
88 const struct element_type elements[] = {
89         { "drive",              CHET_DT },
90         { "picker",             CHET_MT },
91         { "portal",             CHET_IE },
92         { "slot",               CHET_ST },
93         { "voltag",             CHET_VT },      /* Select tapes by barcode */
94         { NULL,                 0 },
95 };
96
97 /* Valid commands. */
98 const struct changer_command commands[] = {
99         { "exchange",           do_exchange },
100         { "getpicker",          do_getpicker },
101         { "ielem",              do_ielem },
102         { "move",               do_move },
103         { "params",             do_params },
104         { "position",           do_position },
105         { "setpicker",          do_setpicker },
106         { "status",             do_status },
107         { "return",             do_return },
108         { "voltag",             do_voltag },
109         { NULL,                 0 },
110 };
111
112 /* Valid special words. */
113 const struct special_word specials[] = {
114         { "inv",                SW_INVERT },
115         { "inv1",               SW_INVERT1 },
116         { "inv2",               SW_INVERT2 },
117         { NULL,                 0 },
118 };
119
120 static  int changer_fd;
121 static  const char *changer_name;
122
123 int
124 main(argc, argv)
125         int argc;
126         char **argv;
127 {
128         int ch, i;
129
130         while ((ch = getopt(argc, argv, "f:")) != -1) {
131                 switch (ch) {
132                 case 'f':
133                         changer_name = optarg;
134                         break;
135
136                 default:
137                         usage();
138                 }
139         }
140         argc -= optind;
141         argv += optind;
142
143         if (argc == 0)
144                 usage();
145
146         /* Get the default changer if not already specified. */
147         if (changer_name == NULL)
148                 if ((changer_name = getenv(CHANGER_ENV_VAR)) == NULL)
149                         changer_name = _PATH_CH;
150
151         /* Open the changer device. */
152         if ((changer_fd = open(changer_name, O_RDWR, 0600)) == -1)
153                 err(1, "%s: open", changer_name);
154
155         /* Register cleanup function. */
156         if (atexit(cleanup))
157                 err(1, "can't register cleanup function");
158
159         /* Find the specified command. */
160         for (i = 0; commands[i].cc_name != NULL; ++i)
161                 if (strcmp(*argv, commands[i].cc_name) == 0)
162                         break;
163         if (commands[i].cc_name == NULL) {
164                 /* look for abbreviation */
165                 for (i = 0; commands[i].cc_name != NULL; ++i)
166                         if (strncmp(*argv, commands[i].cc_name,
167                             strlen(*argv)) == 0)
168                                 break;
169         }
170
171         if (commands[i].cc_name == NULL)
172                 errx(1, "unknown command: %s", *argv);
173
174         exit ((*commands[i].cc_handler)(commands[i].cc_name, argc, argv));
175         /* NOTREACHED */
176 }
177
178 static int
179 do_move(cname, argc, argv)
180         const char *cname;
181         int argc;
182         char **argv;
183 {
184         struct changer_move cmd;
185         int val;
186
187         /*
188          * On a move command, we expect the following:
189          *
190          * <from ET> <from EU> <to ET> <to EU> [inv]
191          *
192          * where ET == element type and EU == element unit.
193          */
194
195         ++argv; --argc;
196
197         if (argc < 4) {
198                 warnx("%s: too few arguments", cname);
199                 goto usage;
200         } else if (argc > 5) {
201                 warnx("%s: too many arguments", cname);
202                 goto usage;
203         }
204         (void) memset(&cmd, 0, sizeof(cmd));
205
206         /* <from ET>  */
207         cmd.cm_fromtype = parse_element_type(*argv);
208         ++argv; --argc;
209
210         /* Check for voltag virtual type */
211         if (CHET_VT == cmd.cm_fromtype) {
212                 find_element(*argv, &cmd.cm_fromtype, &cmd.cm_fromunit);
213         } else {
214                 /* <from EU> */
215                 cmd.cm_fromunit = parse_element_unit(*argv);
216         }
217         ++argv; --argc;
218
219         /* <to ET> */
220         cmd.cm_totype = parse_element_type(*argv);
221         ++argv; --argc;
222
223         /* Check for voltag virtual type, and report error */
224         if (CHET_VT == cmd.cm_totype)
225                 errx(1,"%s: voltag only makes sense as an element source",
226                      cname);
227
228         /* <to EU> */
229         cmd.cm_tounit = parse_element_unit(*argv);
230         ++argv; --argc;
231
232         /* Deal with optional command modifier. */
233         if (argc) {
234                 val = parse_special(*argv);
235                 switch (val) {
236                 case SW_INVERT:
237                         cmd.cm_flags |= CM_INVERT;
238                         break;
239
240                 default:
241                         errx(1, "%s: inappropriate modifier `%s'",
242                             cname, *argv);
243                         /* NOTREACHED */
244                 }
245         }
246
247         /* Send command to changer. */
248         if (ioctl(changer_fd, CHIOMOVE, &cmd))
249                 err(1, "%s: CHIOMOVE", changer_name);
250
251         return (0);
252
253  usage:
254         (void) fprintf(stderr, "usage: %s %s "
255             "<from ET> <from EU> <to ET> <to EU> [inv]\n", __progname, cname);
256         return (1);
257 }
258
259 static int
260 do_exchange(cname, argc, argv)
261         const char *cname;
262         int argc;
263         char **argv;
264 {
265         struct changer_exchange cmd;
266         int val;
267
268         /*
269          * On an exchange command, we expect the following:
270          *
271   * <src ET> <src EU> <dst1 ET> <dst1 EU> [<dst2 ET> <dst2 EU>] [inv1] [inv2]
272          *
273          * where ET == element type and EU == element unit.
274          */
275
276         ++argv; --argc;
277
278         if (argc < 4) {
279                 warnx("%s: too few arguments", cname);
280                 goto usage;
281         } else if (argc > 8) {
282                 warnx("%s: too many arguments", cname);
283                 goto usage;
284         }
285         (void) memset(&cmd, 0, sizeof(cmd));
286
287         /* <src ET>  */
288         cmd.ce_srctype = parse_element_type(*argv);
289         ++argv; --argc;
290
291         /* Check for voltag virtual type */
292         if (CHET_VT == cmd.ce_srctype) {
293                 find_element(*argv, &cmd.ce_srctype, &cmd.ce_srcunit);
294         } else {
295                 /* <from EU> */
296                 cmd.ce_srcunit = parse_element_unit(*argv);
297         }
298         ++argv; --argc;
299
300         /* <dst1 ET> */
301         cmd.ce_fdsttype = parse_element_type(*argv);
302         ++argv; --argc;
303
304         /* Check for voltag virtual type */
305         if (CHET_VT == cmd.ce_fdsttype) {
306                 find_element(*argv, &cmd.ce_fdsttype, &cmd.ce_fdstunit);
307         } else {
308                 /* <from EU> */
309                 cmd.ce_fdstunit = parse_element_unit(*argv);
310         }
311         ++argv; --argc;
312
313         /*
314          * If the next token is a special word or there are no more
315          * arguments, then this is a case of simple exchange.
316          * dst2 == src.
317          */
318         if ((argc == 0) || is_special(*argv)) {
319                 cmd.ce_sdsttype = cmd.ce_srctype;
320                 cmd.ce_sdstunit = cmd.ce_srcunit;
321                 goto do_special;
322         }
323
324         /* <dst2 ET> */
325         cmd.ce_sdsttype = parse_element_type(*argv);
326         ++argv; --argc;
327
328         if (CHET_VT == cmd.ce_sdsttype)
329                 errx(1,"%s %s: voltag only makes sense as an element source",
330                      cname, *argv);
331
332         /* <dst2 EU> */
333         cmd.ce_sdstunit = parse_element_unit(*argv);
334         ++argv; --argc;
335
336  do_special:
337         /* Deal with optional command modifiers. */
338         while (argc) {
339                 val = parse_special(*argv);
340                 ++argv; --argc;
341                 switch (val) {
342                 case SW_INVERT1:
343                         cmd.ce_flags |= CE_INVERT1;
344                         break;
345
346                 case SW_INVERT2:
347                         cmd.ce_flags |= CE_INVERT2;
348                         break;
349
350                 default:
351                         errx(1, "%s: inappropriate modifier `%s'",
352                             cname, *argv);
353                         /* NOTREACHED */
354                 }
355         }
356
357         /* Send command to changer. */
358         if (ioctl(changer_fd, CHIOEXCHANGE, &cmd))
359                 err(1, "%s: CHIOEXCHANGE", changer_name);
360
361         return (0);
362
363  usage:
364         (void) fprintf(stderr,
365             "usage: %s %s <src ET> <src EU> <dst1 ET> <dst1 EU>\n"
366             "       [<dst2 ET> <dst2 EU>] [inv1] [inv2]\n",
367             __progname, cname);
368         return (1);
369 }
370
371 static int
372 do_position(cname, argc, argv)
373         const char *cname;
374         int argc;
375         char **argv;
376 {
377         struct changer_position cmd;
378         int val;
379
380         /*
381          * On a position command, we expect the following:
382          *
383          * <to ET> <to EU> [inv]
384          *
385          * where ET == element type and EU == element unit.
386          */
387
388         ++argv; --argc;
389
390         if (argc < 2) {
391                 warnx("%s: too few arguments", cname);
392                 goto usage;
393         } else if (argc > 3) {
394                 warnx("%s: too many arguments", cname);
395                 goto usage;
396         }
397         (void) memset(&cmd, 0, sizeof(cmd));
398
399         /* <to ET>  */
400         cmd.cp_type = parse_element_type(*argv);
401         ++argv; --argc;
402
403         /* <to EU> */
404         cmd.cp_unit = parse_element_unit(*argv);
405         ++argv; --argc;
406
407         /* Deal with optional command modifier. */
408         if (argc) {
409                 val = parse_special(*argv);
410                 switch (val) {
411                 case SW_INVERT:
412                         cmd.cp_flags |= CP_INVERT;
413                         break;
414
415                 default:
416                         errx(1, "%s: inappropriate modifier `%s'",
417                             cname, *argv);
418                         /* NOTREACHED */
419                 }
420         }
421
422         /* Send command to changer. */
423         if (ioctl(changer_fd, CHIOPOSITION, &cmd))
424                 err(1, "%s: CHIOPOSITION", changer_name);
425
426         return (0);
427
428  usage:
429         (void) fprintf(stderr, "usage: %s %s <to ET> <to EU> [inv]\n",
430             __progname, cname);
431         return (1);
432 }
433
434 /* ARGSUSED */
435 static int
436 do_params(cname, argc, argv)
437         const char *cname;
438         int argc;
439         char **argv;
440 {
441         struct changer_params data;
442         int picker;
443
444         /* No arguments to this command. */
445
446         ++argv; --argc;
447
448         if (argc) {
449                 warnx("%s: no arguments expected", cname);
450                 goto usage;
451         }
452
453         /* Get params from changer and display them. */
454         (void) memset(&data, 0, sizeof(data));
455         if (ioctl(changer_fd, CHIOGPARAMS, &data))
456                 err(1, "%s: CHIOGPARAMS", changer_name);
457
458         (void) printf("%s: %d slot%s, %d drive%s, %d picker%s",
459             changer_name,
460             data.cp_nslots, (data.cp_nslots > 1) ? "s" : "",
461             data.cp_ndrives, (data.cp_ndrives > 1) ? "s" : "",
462             data.cp_npickers, (data.cp_npickers > 1) ? "s" : "");
463         if (data.cp_nportals)
464                 (void) printf(", %d portal%s", data.cp_nportals,
465                     (data.cp_nportals > 1) ? "s" : "");
466
467         /* Get current picker from changer and display it. */
468         if (ioctl(changer_fd, CHIOGPICKER, &picker))
469                 err(1, "%s: CHIOGPICKER", changer_name);
470
471         (void) printf("\n%s: current picker: %d\n", changer_name, picker);
472
473         return (0);
474
475  usage:
476         (void) fprintf(stderr, "usage: %s %s\n", __progname, cname);
477         return (1);
478 }
479
480 /* ARGSUSED */
481 static int
482 do_getpicker(cname, argc, argv)
483         const char *cname;
484         int argc;
485         char **argv;
486 {
487         int picker;
488
489         /* No arguments to this command. */
490
491         ++argv; --argc;
492
493         if (argc) {
494                 warnx("%s: no arguments expected", cname);
495                 goto usage;
496         }
497
498         /* Get current picker from changer and display it. */
499         if (ioctl(changer_fd, CHIOGPICKER, &picker))
500                 err(1, "%s: CHIOGPICKER", changer_name);
501
502         (void) printf("%s: current picker: %d\n", changer_name, picker);
503
504         return (0);
505
506  usage:
507         (void) fprintf(stderr, "usage: %s %s\n", __progname, cname);
508         return (1);
509 }
510
511 static int
512 do_setpicker(cname, argc, argv)
513         const char *cname;
514         int argc;
515         char **argv;
516 {
517         int picker;
518
519         ++argv; --argc;
520
521         if (argc < 1) {
522                 warnx("%s: too few arguments", cname);
523                 goto usage;
524         } else if (argc > 1) {
525                 warnx("%s: too many arguments", cname);
526                 goto usage;
527         }
528
529         picker = parse_element_unit(*argv);
530
531         /* Set the changer picker. */
532         if (ioctl(changer_fd, CHIOSPICKER, &picker))
533                 err(1, "%s: CHIOSPICKER", changer_name);
534
535         return (0);
536
537  usage:
538         (void) fprintf(stderr, "usage: %s %s <picker>\n", __progname, cname);
539         return (1);
540 }
541
542 static int
543 do_status(cname, argc, argv)
544         const char *cname;
545         int argc;
546         char **argv;
547 {
548         struct changer_params cp;
549         struct changer_element_status_request cesr;
550         int i, count, base, chet, schet, echet;
551         const char *description;
552         int pvoltag = 0;
553         int avoltag = 0;
554         int sense = 0;
555         int scsi = 0;
556         int source = 0;
557         int intaddr = 0;
558         int c;
559
560         count = 0;
561         base = 0;
562         description = NULL;
563
564         optind = optreset = 1;
565         while ((c = getopt(argc, argv, "vVsSbaI")) != -1) {
566                 switch (c) {
567                 case 'v':
568                         pvoltag = 1;
569                         break;
570                 case 'V':
571                         avoltag = 1;
572                         break;
573                 case 's':
574                         sense = 1;
575                         break;
576                 case 'S':
577                         source = 1;
578                         break;
579                 case 'b':
580                         scsi = 1;
581                         break;
582                 case 'I':
583                         intaddr = 1;
584                         break;
585                 case 'a':
586                         pvoltag = avoltag = source = sense = scsi = intaddr = 1;
587                         break;
588                 default:
589                         warnx("%s: bad option", cname);
590                         goto usage;
591                 }
592         }
593
594         argc -= optind;
595         argv += optind;
596
597         /*
598          * On a status command, we expect the following:
599          *
600          * [<ET> [<start> [<end>] ] ]
601          *
602          * where ET == element type, start == first element to report,
603          * end == number of elements to report
604          *
605          * If we get no arguments, we get the status of all
606          * known element types.
607          */
608         if (argc > 3) {
609                 warnx("%s: too many arguments", cname);
610                 goto usage;
611         }
612
613         /*
614          * Get params from changer.  Specifically, we need the element
615          * counts.
616          */
617         if (ioctl(changer_fd, CHIOGPARAMS, (char *)&cp))
618                 err(1, "%s: CHIOGPARAMS", changer_name);
619
620         if (argc > 0)
621                 schet = echet = parse_element_type(argv[0]);
622         else {
623                 schet = CHET_MT;
624                 echet = CHET_DT;
625         }
626         if (argc > 1) {
627                 base = atol(argv[1]);
628                 count = 1;
629         }
630         if (argc > 2)
631                 count = atol(argv[2]) - base + 1;
632
633         if (base < 0 || count < 0)
634                 errx(1, "bad arguments");
635
636         for (chet = schet; chet <= echet; ++chet) {
637                 switch (chet) {
638                 case CHET_MT:
639                         if (count == 0) 
640                                 count = cp.cp_npickers;
641                         else if (count > cp.cp_npickers)
642                                 errx(1, "not that many pickers in device");
643                         description = "picker";
644                         break;
645
646                 case CHET_ST:
647                         if (count == 0) 
648                                 count = cp.cp_nslots;
649                         else if (count > cp.cp_nslots)
650                                 errx(1, "not that many slots in device");
651                         description = "slot";
652                         break;
653
654                 case CHET_IE:
655                         if (count == 0) 
656                                 count = cp.cp_nportals;
657                         else if (count > cp.cp_nportals)
658                                 errx(1, "not that many portals in device");
659                         description = "portal";
660                         break;
661
662                 case CHET_DT:
663                         if (count == 0) 
664                                 count = cp.cp_ndrives;
665                         else if (count > cp.cp_ndrives)
666                                 errx(1, "not that many drives in device");
667                         description = "drive";
668                         break;
669  
670                 default:
671                         /* To appease gcc -Wuninitialized. */
672                         count = 0;
673                         description = NULL;
674                 }
675
676                 if (count == 0) {
677                         if (argc == 0)
678                                 continue;
679                         else {
680                                 printf("%s: no %s elements\n",
681                                     changer_name, description);
682                                 return (0);
683                         }
684                 }
685
686                 bzero(&cesr, sizeof(cesr));
687                 cesr.cesr_element_type = chet;
688                 cesr.cesr_element_base = base;
689                 cesr.cesr_element_count = count;
690                 /* Allocate storage for the status structures. */
691                 cesr.cesr_element_status =
692                   (struct changer_element_status *) 
693                   calloc((size_t)count, sizeof(struct changer_element_status));
694                 
695                 if (!cesr.cesr_element_status)
696                         errx(1, "can't allocate status storage");
697
698                 if (avoltag || pvoltag)
699                         cesr.cesr_flags |= CESR_VOLTAGS;
700
701                 if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr)) {
702                         free(cesr.cesr_element_status);
703                         err(1, "%s: CHIOGSTATUS", changer_name);
704                 }
705
706                 /* Dump the status for each reported element. */
707                 for (i = 0; i < count; ++i) {
708                         struct changer_element_status *ces =
709                                  &(cesr.cesr_element_status[i]);
710                         printf("%s %d: %s", description, ces->ces_addr,
711                             bits_to_string(ces->ces_flags,
712                                            CESTATUS_BITS));
713                         if (sense)
714                                 printf(" sense: <0x%02x/0x%02x>",
715                                        ces->ces_sensecode, 
716                                        ces->ces_sensequal);
717                         if (pvoltag)
718                                 printf(" voltag: <%s:%d>", 
719                                        ces->ces_pvoltag.cv_volid,
720                                        ces->ces_pvoltag.cv_serial);
721                         if (avoltag)
722                                 printf(" avoltag: <%s:%d>", 
723                                        ces->ces_avoltag.cv_volid,
724                                        ces->ces_avoltag.cv_serial);
725                         if (source) {
726                                 if (ces->ces_flags & CES_SOURCE_VALID)
727                                         printf(" source: <%s %d>", 
728                                                element_type_name(
729                                                        ces->ces_source_type),
730                                                ces->ces_source_addr);
731                                 else
732                                         printf(" source: <>");
733                         }
734                         if (intaddr)
735                                 printf(" intaddr: <%d>", ces->ces_int_addr);
736                         if (scsi) {
737                                 printf(" scsi: <");
738                                 if (ces->ces_flags & CES_SCSIID_VALID)
739                                         printf("%d", ces->ces_scsi_id);
740                                 else
741                                         putchar('?');
742                                 putchar(':');
743                                 if (ces->ces_flags & CES_LUN_VALID)
744                                         printf("%d", ces->ces_scsi_lun);
745                                 else
746                                         putchar('?');
747                                 putchar('>');
748                         }
749                         putchar('\n');
750                 }
751
752                 free(cesr.cesr_element_status);
753                 count = 0;
754         }
755
756         return (0);
757
758  usage:
759         (void) fprintf(stderr, "usage: %s %s [-vVsSbaA] [<element type> [<start-addr> [<end-addr>] ] ]\n",
760                        __progname, cname);
761         return (1);
762 }
763
764 static int
765 do_ielem(cname, argc, argv)
766         const char *cname;
767         int argc;
768         char **argv;
769 {
770         int timeout = 0;
771
772         if (argc == 2) {
773                 timeout = atol(argv[1]);
774         } else if (argc > 1) {
775                 warnx("%s: too many arguments", cname);
776                 goto usage;
777         }
778
779         if (ioctl(changer_fd, CHIOIELEM, &timeout))
780                 err(1, "%s: CHIOIELEM", changer_name);
781
782         return (0);
783
784  usage:
785         (void) fprintf(stderr, "usage: %s %s [<timeout>]\n",
786                        __progname, cname);
787         return (1);
788 }
789
790 static int
791 do_voltag(cname, argc, argv)
792         const char *cname;
793         int argc;
794         char **argv;
795 {
796         int force = 0;
797         int clear = 0;
798         int alternate = 0;
799         int c;
800         struct changer_set_voltag_request csvr;
801
802         bzero(&csvr, sizeof(csvr));
803
804         optind = optreset = 1;
805         while ((c = getopt(argc, argv, "fca")) != -1) {
806                 switch (c) {
807                 case 'f':
808                         force = 1;
809                         break;
810                 case 'c':
811                         clear = 1;
812                         break;
813                 case 'a':
814                         alternate = 1;
815                         break;
816                 default:
817                         warnx("%s: bad option", cname);
818                         goto usage;
819                 }
820         }
821
822         argc -= optind;
823         argv += optind;
824
825         if (argc < 2) {
826                 warnx("%s: missing element specification", cname);
827                 goto usage;
828         }
829
830         csvr.csvr_type = parse_element_type(argv[0]);
831         csvr.csvr_addr = atol(argv[1]);
832
833         if (!clear) {
834                 if (argc < 3 || argc > 4) {
835                         warnx("%s: missing argument", cname);
836                         goto usage;
837                 }
838
839                 if (force)
840                         csvr.csvr_flags = CSVR_MODE_REPLACE;
841                 else
842                         csvr.csvr_flags = CSVR_MODE_SET;
843
844                 if (strlen(argv[2]) > sizeof(csvr.csvr_voltag.cv_volid)) {
845                         warnx("%s: volume label too long", cname);
846                         goto usage;
847                 }
848
849                 strlcpy((char *)csvr.csvr_voltag.cv_volid, argv[2],
850                        sizeof(csvr.csvr_voltag.cv_volid));
851
852                 if (argc == 4) {
853                         csvr.csvr_voltag.cv_serial = atol(argv[3]);
854                 }
855         } else {
856                 if (argc != 2) {
857                         warnx("%s: unexpected argument", cname);
858                         goto usage;
859                 }
860                 csvr.csvr_flags = CSVR_MODE_CLEAR;
861         }
862
863         if (alternate) {
864                 csvr.csvr_flags |= CSVR_ALTERNATE;
865         }
866
867         if (ioctl(changer_fd, CHIOSETVOLTAG, &csvr))
868                 err(1, "%s: CHIOSETVOLTAG", changer_name);
869
870         return 0;
871  usage:
872         (void) fprintf(stderr, 
873                        "usage: %s %s [-fca] <element> [<voltag> [<vsn>] ]\n",
874                        __progname, cname);
875         return 1;
876 }
877
878 static int
879 parse_element_type(cp)
880         char *cp;
881 {
882         int i;
883
884         for (i = 0; elements[i].et_name != NULL; ++i)
885                 if (strcmp(elements[i].et_name, cp) == 0)
886                         return (elements[i].et_type);
887
888         errx(1, "invalid element type `%s'", cp);
889         /* NOTREACHED */
890 }
891
892 static const char *
893 element_type_name(et)
894         int et;
895 {
896         int i;
897
898         for (i = 0; elements[i].et_name != NULL; i++)
899                 if (elements[i].et_type == et)
900                         return elements[i].et_name;
901
902         return "unknown";
903 }
904
905 static int
906 parse_element_unit(cp)
907         char *cp;
908 {
909         int i;
910         char *p;
911
912         i = (int)strtol(cp, &p, 10);
913         if ((i < 0) || (*p != '\0'))
914                 errx(1, "invalid unit number `%s'", cp);
915
916         return (i);
917 }
918
919 static int
920 parse_special(cp)
921         char *cp;
922 {
923         int val;
924
925         val = is_special(cp);
926         if (val)
927                 return (val);
928
929         errx(1, "invalid modifier `%s'", cp);
930         /* NOTREACHED */
931 }
932
933 static int
934 is_special(cp)
935         char *cp;
936 {
937         int i;
938
939         for (i = 0; specials[i].sw_name != NULL; ++i)
940                 if (strcmp(specials[i].sw_name, cp) == 0)
941                         return (specials[i].sw_value);
942
943         return (0);
944 }
945
946 static const char *
947 bits_to_string(v, cp)
948         ces_status_flags v;
949         const char *cp;
950 {
951         const char *np;
952         char f, sep, *bp;
953         static char buf[128];
954
955         bp = buf;
956         (void) memset(buf, 0, sizeof(buf));
957
958         for (sep = '<'; (f = *cp++) != 0; cp = np) {
959                 for (np = cp; *np >= ' ';)
960                         np++;
961                 if ((v & (1 << (f - 1))) == 0)
962                         continue;
963                 (void) snprintf(bp, sizeof(buf) - (bp - &buf[0]),
964                         "%c%.*s", sep, (int)(long)(np - cp), cp);
965                 bp += strlen(bp);
966                 sep = ',';
967         }
968         if (sep != '<')
969                 *bp = '>';
970
971         return (buf);
972 }
973 /*
974  * do_return()
975  * 
976  * Given an element reference, ask the changer/picker to move that
977  * element back to its source slot.
978  */
979 static int
980 do_return(cname, argc, argv)
981         const char *cname;
982         int  argc;
983         char **argv;
984 {
985         struct changer_element_status *ces;
986         struct changer_move cmd;
987         u_int16_t       type, element;
988
989         ++argv; --argc;
990
991         if (argc < 2) {
992                 warnx("%s: too few arguments", cname);
993                 goto usage;
994         } else if (argc > 3) {
995                 warnx("%s: too many arguments", cname);
996                 goto usage;
997         }
998
999         type = parse_element_type(*argv);
1000         ++argv; --argc;
1001         
1002         /* Handle voltag virtual Changer Element Type */
1003         if (CHET_VT == type) {
1004                 find_element(*argv, &type, &element);
1005         } else {
1006                 element = parse_element_unit(*argv);
1007         }
1008         ++argv; --argc;
1009
1010         /* Get the status */
1011         ces = get_element_status((unsigned int)type, (unsigned int)element);
1012
1013         if (NULL == ces)
1014                 errx(1, "%s: null element status pointer", cname);
1015
1016         if (!(ces->ces_flags & CES_SOURCE_VALID))
1017                 errx(1, "%s: no source information", cname);
1018
1019         (void) memset(&cmd, 0, sizeof(cmd));
1020
1021         cmd.cm_fromtype = type;
1022         cmd.cm_fromunit = element;
1023         cmd.cm_totype = ces->ces_source_type;
1024         cmd.cm_tounit = ces->ces_source_addr;
1025
1026         if (ioctl(changer_fd, CHIOMOVE, &cmd) == -1)
1027                 err(1, "%s: CHIOMOVE", changer_name);
1028         free(ces);
1029
1030         return(0);
1031
1032 usage:
1033         (void) fprintf(stderr, "usage: %s %s "
1034             "<from ET> <from EU>\n", __progname, cname);
1035         return(1);
1036 }
1037
1038 /*
1039  * get_element_status()
1040  *
1041  * return a *cesr for the specified changer element.  This
1042  * routing will malloc()/calloc() the memory.  The caller
1043  * should free() it when done.
1044  */
1045 static struct changer_element_status *
1046 get_element_status(type, element)
1047         unsigned int    type;
1048         unsigned int    element;
1049 {
1050         struct changer_element_status_request cesr;
1051         struct changer_element_status *ces;
1052         
1053         ces = (struct changer_element_status *)
1054             calloc((size_t)1, sizeof(struct changer_element_status));
1055
1056         if (NULL == ces)
1057                 errx(1, "can't allocate status storage");
1058
1059         (void)memset(&cesr, 0, sizeof(cesr));
1060
1061         cesr.cesr_element_type = (u_int16_t)type;
1062         cesr.cesr_element_base = (u_int16_t)element;
1063         cesr.cesr_element_count = 1;            /* Only this one element */
1064         cesr.cesr_flags |= CESR_VOLTAGS;        /* Grab voltags as well */
1065         cesr.cesr_element_status = ces;
1066
1067         if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) {
1068                 free(ces);
1069                 err(1, "%s: CHIOGSTATUS", changer_name);
1070                 /* NOTREACHED */
1071         }
1072
1073         return ces;
1074 }
1075
1076
1077 /*
1078  * find_element()
1079  * 
1080  * Given a <voltag> find the chager element and unit, or exit
1081  * with an error if it isn't found.  We grab the changer status
1082  * and iterate until we find a match, or crap out.
1083  */
1084 static void
1085 find_element(voltag, et, eu)
1086         char *voltag;
1087         u_int16_t *et;
1088         u_int16_t *eu;
1089 {
1090         struct changer_params cp;
1091         struct changer_element_status_request cesr;
1092         struct changer_element_status *ch_ces, *ces;
1093         int found = 0;
1094         size_t elem, total_elem;
1095
1096         /*
1097          * Get the changer parameters, we're interested in the counts
1098          * for all types of elements to perform our search.
1099          */
1100         if (ioctl(changer_fd, CHIOGPARAMS, (char *)&cp))
1101                 err(1, "%s: CHIOGPARAMS", changer_name);
1102
1103         /* Allocate some memory for the results */
1104         total_elem = (cp.cp_nslots + cp.cp_ndrives
1105             + cp.cp_npickers + cp.cp_nportals);
1106         
1107         ch_ces = (struct changer_element_status *)
1108             calloc(total_elem, sizeof(struct changer_element_status));
1109
1110         if (NULL == ch_ces)
1111                 errx(1, "can't allocate status storage");
1112
1113         ces = ch_ces;
1114
1115         /* Read in the changer slots */
1116         if (cp.cp_nslots > 0) {
1117                 cesr.cesr_element_type = CHET_ST;
1118                 cesr.cesr_element_base = 0;
1119                 cesr.cesr_element_count = cp.cp_nslots;
1120                 cesr.cesr_flags |= CESR_VOLTAGS;
1121                 cesr.cesr_element_status = ces;
1122
1123                 if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) {
1124                         free(ch_ces);
1125                         err(1, "%s: CHIOGSTATUS", changer_name);
1126                 }
1127                 ces += cp.cp_nslots;
1128         }       
1129
1130         /* Read in the drive information */
1131         if (cp.cp_ndrives > 0 ) {
1132
1133                 (void) memset(&cesr, 0, sizeof(cesr));
1134                 cesr.cesr_element_type = CHET_DT;
1135                 cesr.cesr_element_base = 0;
1136                 cesr.cesr_element_count = cp.cp_ndrives;
1137                 cesr.cesr_flags |= CESR_VOLTAGS;
1138                 cesr.cesr_element_status = ces;
1139
1140                 if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) {
1141                         free(ch_ces);
1142                         err(1, "%s: CHIOGSTATUS", changer_name);
1143                 }
1144                 ces += cp.cp_ndrives;
1145         }
1146
1147         /* Read in the portal information */
1148         if (cp.cp_nportals > 0 ) {
1149                 (void) memset(&cesr, 0, sizeof(cesr));
1150                 cesr.cesr_element_type = CHET_IE;
1151                 cesr.cesr_element_base = 0;
1152                 cesr.cesr_element_count = cp.cp_nportals;
1153                 cesr.cesr_flags |= CESR_VOLTAGS;
1154                 cesr.cesr_element_status = ces;
1155
1156                 if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) {
1157                         free(ch_ces);
1158                         err(1, "%s: CHIOGSTATUS", changer_name);
1159                 }
1160                 ces += cp.cp_nportals;
1161         }
1162
1163         /* Read in the picker information */
1164         if (cp.cp_npickers > 0) {
1165                 (void) memset(&cesr, 0, sizeof(cesr));
1166                 cesr.cesr_element_type = CHET_MT;
1167                 cesr.cesr_element_base = 0;
1168                 cesr.cesr_element_count = cp.cp_npickers;
1169                 cesr.cesr_flags |= CESR_VOLTAGS;
1170                 cesr.cesr_element_status = ces;
1171
1172                 if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) {
1173                         free(ch_ces);
1174                         err(1, "%s: CHIOGSTATUS", changer_name);
1175                 }
1176         }
1177
1178         /*
1179          * Now search the list the specified <voltag>
1180          */     
1181         for (elem = 0; elem <= total_elem; ++elem) {
1182
1183                 ces = &ch_ces[elem];
1184
1185                 /* Make sure we have a tape in this element */
1186                 if ((ces->ces_flags & (CES_STATUS_ACCESS|CES_STATUS_FULL))
1187                     != (CES_STATUS_ACCESS|CES_STATUS_FULL))
1188                         continue;
1189
1190                 /* Check to see if it is our target */
1191                 if (strcasecmp(voltag,
1192                     (const char *)ces->ces_pvoltag.cv_volid) == 0) {
1193                         *et = ces->ces_type;
1194                         *eu = ces->ces_addr;
1195                         ++found;
1196                         break;
1197                 }
1198         }
1199         if (!found) {
1200                 errx(1, "%s: unable to locate voltag: %s", changer_name,
1201                      voltag);
1202         }
1203         free(ch_ces);
1204         return;
1205 }
1206
1207 static void
1208 cleanup()
1209 {
1210         /* Simple enough... */
1211         (void)close(changer_fd);
1212 }
1213
1214 static void
1215 usage()
1216 {
1217         (void) fprintf(stderr, "usage: %s [-f changer] command [-<flags>] "
1218                 "arg1 arg2 [arg3 [...]]\n", __progname);
1219         exit(1);
1220 }