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