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