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