]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - bin/chio/chio.c
libsys: consolidate PSEUDO definitions
[FreeBSD/FreeBSD.git] / bin / chio / chio.c
1 /*      $NetBSD: chio.c,v 1.6 1998/01/04 23:53:58 thorpej Exp $ */
2 /*-
3  * Copyright (c) 1996 Jason R. Thorpe <thorpej@and.com>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgements:
16  *      This product includes software developed by Jason R. Thorpe
17  *      for And Communications, http://www.and.com/
18  * 4. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 /*
34  * Additional Copyright (c) 1997, by Matthew Jacob, for NASA/Ames Research Ctr.
35  * Additional Copyright (c) 2000, by C. Stephen Gunn, Waterspout Communications
36  */
37
38 #include <sys/param.h>
39 #include <sys/chio.h> 
40 #include <err.h>
41 #include <fcntl.h>
42 #include <stdio.h>
43 #include <stdint.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47 #include <langinfo.h>
48 #include <locale.h>
49
50 #include "defs.h"
51 #include "pathnames.h"
52
53 static  void usage(void) __dead2;
54 static  void cleanup(void);
55 static  u_int16_t parse_element_type(char *);
56 static  u_int16_t parse_element_unit(char *);
57 static  const char * element_type_name(int et);
58 static  int parse_special(char *);
59 static  int is_special(char *);
60 static  const char *bits_to_string(ces_status_flags, const char *);
61
62 static  void find_element(char *, uint16_t *, uint16_t *);
63 static  struct changer_element_status *get_element_status
64            (unsigned int, unsigned int, int);
65
66 static  int do_move(const char *, int, char **);
67 static  int do_exchange(const char *, int, char **);
68 static  int do_position(const char *, int, char **);
69 static  int do_params(const char *, int, char **);
70 static  int do_getpicker(const char *, int, char **);
71 static  int do_setpicker(const char *, int, char **);
72 static  int do_status(const char *, int, char **);
73 static  int do_ielem(const char *, int, char **);
74 static  int do_return(const char *, int, char **);
75 static  int do_voltag(const char *, int, char **);
76 static  void print_designator(const char *, u_int8_t, u_int8_t);
77
78 #ifndef CHET_VT
79 #define CHET_VT         10                      /* Completely Arbitrary */
80 #endif
81
82 /* Valid changer element types. */
83 static  const struct element_type elements[] = {
84         { "drive",              CHET_DT },
85         { "picker",             CHET_MT },
86         { "portal",             CHET_IE },
87         { "slot",               CHET_ST },
88         { "voltag",             CHET_VT },      /* Select tapes by barcode */
89         { NULL,                 0 },
90 };
91
92 /* Valid commands. */
93 static  const struct changer_command commands[] = {
94         { "exchange",           do_exchange },
95         { "getpicker",          do_getpicker },
96         { "ielem",              do_ielem },
97         { "move",               do_move },
98         { "params",             do_params },
99         { "position",           do_position },
100         { "setpicker",          do_setpicker },
101         { "status",             do_status },
102         { "return",             do_return },
103         { "voltag",             do_voltag },
104         { NULL,                 0 },
105 };
106
107 /* Valid special words. */
108 static  const struct special_word specials[] = {
109         { "inv",                SW_INVERT },
110         { "inv1",               SW_INVERT1 },
111         { "inv2",               SW_INVERT2 },
112         { NULL,                 0 },
113 };
114
115 static  int changer_fd;
116 static  const char *changer_name;
117
118 int
119 main(int argc, char **argv)
120 {
121         int ch, i;
122
123         while ((ch = getopt(argc, argv, "f:")) != -1) {
124                 switch (ch) {
125                 case 'f':
126                         changer_name = optarg;
127                         break;
128
129                 default:
130                         usage();
131                 }
132         }
133         argc -= optind;
134         argv += optind;
135
136         if (argc == 0)
137                 usage();
138
139         /* Get the default changer if not already specified. */
140         if (changer_name == NULL)
141                 if ((changer_name = getenv(CHANGER_ENV_VAR)) == NULL)
142                         changer_name = _PATH_CH;
143
144         /* Open the changer device. */
145         if ((changer_fd = open(changer_name, O_RDWR, 0600)) == -1)
146                 err(1, "%s: open", changer_name);
147
148         /* Register cleanup function. */
149         if (atexit(cleanup))
150                 err(1, "can't register cleanup function");
151
152         /* Find the specified command. */
153         for (i = 0; commands[i].cc_name != NULL; ++i)
154                 if (strcmp(*argv, commands[i].cc_name) == 0)
155                         break;
156         if (commands[i].cc_name == NULL) {
157                 /* look for abbreviation */
158                 for (i = 0; commands[i].cc_name != NULL; ++i)
159                         if (strncmp(*argv, commands[i].cc_name,
160                             strlen(*argv)) == 0)
161                                 break;
162         }
163
164         if (commands[i].cc_name == NULL)
165                 errx(1, "unknown command: %s", *argv);
166
167         exit ((*commands[i].cc_handler)(commands[i].cc_name, argc, argv));
168         /* NOTREACHED */
169 }
170
171 static int
172 do_move(const char *cname, int argc, char **argv)
173 {
174         struct changer_move cmd;
175         int val;
176
177         /*
178          * On a move command, we expect the following:
179          *
180          * <from ET> <from EU> <to ET> <to EU> [inv]
181          *
182          * where ET == element type and EU == element unit.
183          */
184
185         ++argv; --argc;
186
187         if (argc < 4) {
188                 warnx("%s: too few arguments", cname);
189                 goto usage;
190         } else if (argc > 5) {
191                 warnx("%s: too many arguments", cname);
192                 goto usage;
193         }
194         (void) memset(&cmd, 0, sizeof(cmd));
195
196         /* <from ET>  */
197         cmd.cm_fromtype = parse_element_type(*argv);
198         ++argv; --argc;
199
200         /* Check for voltag virtual type */
201         if (CHET_VT == cmd.cm_fromtype) {
202                 find_element(*argv, &cmd.cm_fromtype, &cmd.cm_fromunit);
203         } else {
204                 /* <from EU> */
205                 cmd.cm_fromunit = parse_element_unit(*argv);
206         }
207         ++argv; --argc;
208
209         /* <to ET> */
210         cmd.cm_totype = parse_element_type(*argv);
211         ++argv; --argc;
212
213         /* Check for voltag virtual type, and report error */
214         if (CHET_VT == cmd.cm_totype)
215                 errx(1,"%s: voltag only makes sense as an element source",
216                      cname);
217
218         /* <to EU> */
219         cmd.cm_tounit = parse_element_unit(*argv);
220         ++argv; --argc;
221
222         /* Deal with optional command modifier. */
223         if (argc) {
224                 val = parse_special(*argv);
225                 switch (val) {
226                 case SW_INVERT:
227                         cmd.cm_flags |= CM_INVERT;
228                         break;
229
230                 default:
231                         errx(1, "%s: inappropriate modifier `%s'",
232                             cname, *argv);
233                         /* NOTREACHED */
234                 }
235         }
236
237         /* Send command to changer. */
238         if (ioctl(changer_fd, CHIOMOVE, &cmd))
239                 err(1, "%s: CHIOMOVE", changer_name);
240
241         return (0);
242
243  usage:
244         (void) fprintf(stderr, "usage: %s %s "
245             "<from ET> <from EU> <to ET> <to EU> [inv]\n", getprogname(), cname);
246         return (1);
247 }
248
249 static int
250 do_exchange(const char *cname, int argc, char **argv)
251 {
252         struct changer_exchange cmd;
253         int val;
254
255         /*
256          * On an exchange command, we expect the following:
257          *
258   * <src ET> <src EU> <dst1 ET> <dst1 EU> [<dst2 ET> <dst2 EU>] [inv1] [inv2]
259          *
260          * where ET == element type and EU == element unit.
261          */
262
263         ++argv; --argc;
264
265         if (argc < 4) {
266                 warnx("%s: too few arguments", cname);
267                 goto usage;
268         } else if (argc > 8) {
269                 warnx("%s: too many arguments", cname);
270                 goto usage;
271         }
272         (void) memset(&cmd, 0, sizeof(cmd));
273
274         /* <src ET>  */
275         cmd.ce_srctype = parse_element_type(*argv);
276         ++argv; --argc;
277
278         /* Check for voltag virtual type */
279         if (CHET_VT == cmd.ce_srctype) {
280                 find_element(*argv, &cmd.ce_srctype, &cmd.ce_srcunit);
281         } else {
282                 /* <from EU> */
283                 cmd.ce_srcunit = parse_element_unit(*argv);
284         }
285         ++argv; --argc;
286
287         /* <dst1 ET> */
288         cmd.ce_fdsttype = parse_element_type(*argv);
289         ++argv; --argc;
290
291         /* Check for voltag virtual type */
292         if (CHET_VT == cmd.ce_fdsttype) {
293                 find_element(*argv, &cmd.ce_fdsttype, &cmd.ce_fdstunit);
294         } else {
295                 /* <from EU> */
296                 cmd.ce_fdstunit = parse_element_unit(*argv);
297         }
298         ++argv; --argc;
299
300         /*
301          * If the next token is a special word or there are no more
302          * arguments, then this is a case of simple exchange.
303          * dst2 == src.
304          */
305         if ((argc == 0) || is_special(*argv)) {
306                 cmd.ce_sdsttype = cmd.ce_srctype;
307                 cmd.ce_sdstunit = cmd.ce_srcunit;
308                 goto do_special;
309         }
310
311         /* <dst2 ET> */
312         cmd.ce_sdsttype = parse_element_type(*argv);
313         ++argv; --argc;
314
315         if (CHET_VT == cmd.ce_sdsttype)
316                 errx(1,"%s %s: voltag only makes sense as an element source",
317                      cname, *argv);
318
319         /* <dst2 EU> */
320         cmd.ce_sdstunit = parse_element_unit(*argv);
321         ++argv; --argc;
322
323  do_special:
324         /* Deal with optional command modifiers. */
325         while (argc) {
326                 val = parse_special(*argv);
327                 ++argv; --argc;
328                 switch (val) {
329                 case SW_INVERT1:
330                         cmd.ce_flags |= CE_INVERT1;
331                         break;
332
333                 case SW_INVERT2:
334                         cmd.ce_flags |= CE_INVERT2;
335                         break;
336
337                 default:
338                         errx(1, "%s: inappropriate modifier `%s'",
339                             cname, *argv);
340                         /* NOTREACHED */
341                 }
342         }
343
344         /* Send command to changer. */
345         if (ioctl(changer_fd, CHIOEXCHANGE, &cmd))
346                 err(1, "%s: CHIOEXCHANGE", changer_name);
347
348         return (0);
349
350  usage:
351         (void) fprintf(stderr,
352             "usage: %s %s <src ET> <src EU> <dst1 ET> <dst1 EU>\n"
353             "       [<dst2 ET> <dst2 EU>] [inv1] [inv2]\n",
354             getprogname(), cname);
355         return (1);
356 }
357
358 static int
359 do_position(const char *cname, int argc, char **argv)
360 {
361         struct changer_position cmd;
362         int val;
363
364         /*
365          * On a position command, we expect the following:
366          *
367          * <to ET> <to EU> [inv]
368          *
369          * where ET == element type and EU == element unit.
370          */
371
372         ++argv; --argc;
373
374         if (argc < 2) {
375                 warnx("%s: too few arguments", cname);
376                 goto usage;
377         } else if (argc > 3) {
378                 warnx("%s: too many arguments", cname);
379                 goto usage;
380         }
381         (void) memset(&cmd, 0, sizeof(cmd));
382
383         /* <to ET>  */
384         cmd.cp_type = parse_element_type(*argv);
385         ++argv; --argc;
386
387         /* <to EU> */
388         cmd.cp_unit = parse_element_unit(*argv);
389         ++argv; --argc;
390
391         /* Deal with optional command modifier. */
392         if (argc) {
393                 val = parse_special(*argv);
394                 switch (val) {
395                 case SW_INVERT:
396                         cmd.cp_flags |= CP_INVERT;
397                         break;
398
399                 default:
400                         errx(1, "%s: inappropriate modifier `%s'",
401                             cname, *argv);
402                         /* NOTREACHED */
403                 }
404         }
405
406         /* Send command to changer. */
407         if (ioctl(changer_fd, CHIOPOSITION, &cmd))
408                 err(1, "%s: CHIOPOSITION", changer_name);
409
410         return (0);
411
412  usage:
413         (void) fprintf(stderr, "usage: %s %s <to ET> <to EU> [inv]\n",
414             getprogname(), cname);
415         return (1);
416 }
417
418 /* ARGSUSED */
419 static int
420 do_params(const char *cname, int argc, char **argv __unused)
421 {
422         struct changer_params data;
423         int picker;
424
425         /* No arguments to this command. */
426
427         ++argv; --argc;
428
429         if (argc) {
430                 warnx("%s: no arguments expected", cname);
431                 goto usage;
432         }
433
434         /* Get params from changer and display them. */
435         (void) memset(&data, 0, sizeof(data));
436         if (ioctl(changer_fd, CHIOGPARAMS, &data))
437                 err(1, "%s: CHIOGPARAMS", changer_name);
438
439         (void) printf("%s: %d slot%s, %d drive%s, %d picker%s",
440             changer_name,
441             data.cp_nslots, (data.cp_nslots > 1) ? "s" : "",
442             data.cp_ndrives, (data.cp_ndrives > 1) ? "s" : "",
443             data.cp_npickers, (data.cp_npickers > 1) ? "s" : "");
444         if (data.cp_nportals)
445                 (void) printf(", %d portal%s", data.cp_nportals,
446                     (data.cp_nportals > 1) ? "s" : "");
447
448         /* Get current picker from changer and display it. */
449         if (ioctl(changer_fd, CHIOGPICKER, &picker))
450                 err(1, "%s: CHIOGPICKER", changer_name);
451
452         (void) printf("\n%s: current picker: %d\n", changer_name, picker);
453
454         return (0);
455
456  usage:
457         (void) fprintf(stderr, "usage: %s %s\n", getprogname(), cname);
458         return (1);
459 }
460
461 /* ARGSUSED */
462 static int
463 do_getpicker(const char *cname, int argc, char **argv __unused)
464 {
465         int picker;
466
467         /* No arguments to this command. */
468
469         ++argv; --argc;
470
471         if (argc) {
472                 warnx("%s: no arguments expected", cname);
473                 goto usage;
474         }
475
476         /* Get current picker from changer and display it. */
477         if (ioctl(changer_fd, CHIOGPICKER, &picker))
478                 err(1, "%s: CHIOGPICKER", changer_name);
479
480         (void) printf("%s: current picker: %d\n", changer_name, picker);
481
482         return (0);
483
484  usage:
485         (void) fprintf(stderr, "usage: %s %s\n", getprogname(), cname);
486         return (1);
487 }
488
489 static int
490 do_setpicker(const char *cname, int argc, char **argv)
491 {
492         int picker;
493
494         ++argv; --argc;
495
496         if (argc < 1) {
497                 warnx("%s: too few arguments", cname);
498                 goto usage;
499         } else if (argc > 1) {
500                 warnx("%s: too many arguments", cname);
501                 goto usage;
502         }
503
504         picker = parse_element_unit(*argv);
505
506         /* Set the changer picker. */
507         if (ioctl(changer_fd, CHIOSPICKER, &picker))
508                 err(1, "%s: CHIOSPICKER", changer_name);
509
510         return (0);
511
512  usage:
513         (void) fprintf(stderr, "usage: %s %s <picker>\n", getprogname(), cname);
514         return (1);
515 }
516
517 static int
518 do_status(const char *cname, int argc, char **argv)
519 {
520         struct changer_params cp;
521         struct changer_element_status_request cesr;
522         int i;
523         u_int16_t base, count, 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 = (u_int16_t)atol(argv[1]);
601                 count = 1;
602         }
603         if (argc > 2)
604                 count = (u_int16_t)atol(argv[2]) - base + 1;
605
606         for (chet = schet; chet <= echet; ++chet) {
607                 switch (chet) {
608                 case CHET_MT:
609                         if (count == 0) 
610                                 count = cp.cp_npickers;
611                         else if (count > cp.cp_npickers)
612                                 errx(1, "not that many pickers in device");
613                         description = "picker";
614                         break;
615
616                 case CHET_ST:
617                         if (count == 0) 
618                                 count = cp.cp_nslots;
619                         else if (count > cp.cp_nslots)
620                                 errx(1, "not that many slots in device");
621                         description = "slot";
622                         break;
623
624                 case CHET_IE:
625                         if (count == 0) 
626                                 count = cp.cp_nportals;
627                         else if (count > cp.cp_nportals)
628                                 errx(1, "not that many portals in device");
629                         description = "portal";
630                         break;
631
632                 case CHET_DT:
633                         if (count == 0) 
634                                 count = cp.cp_ndrives;
635                         else if (count > cp.cp_ndrives)
636                                 errx(1, "not that many drives in device");
637                         description = "drive";
638                         break;
639  
640                 default:
641                         /* To appease gcc -Wuninitialized. */
642                         count = 0;
643                         description = NULL;
644                 }
645
646                 if (count == 0) {
647                         if (argc == 0)
648                                 continue;
649                         else {
650                                 printf("%s: no %s elements\n",
651                                     changer_name, description);
652                                 return (0);
653                         }
654                 }
655
656                 bzero(&cesr, sizeof(cesr));
657                 cesr.cesr_element_type = chet;
658                 cesr.cesr_element_base = base;
659                 cesr.cesr_element_count = count;
660                 /* Allocate storage for the status structures. */
661                 cesr.cesr_element_status =
662                   (struct changer_element_status *) 
663                   calloc((size_t)count, sizeof(struct changer_element_status));
664                 
665                 if (!cesr.cesr_element_status)
666                         errx(1, "can't allocate status storage");
667
668                 if (avoltag || pvoltag)
669                         cesr.cesr_flags |= CESR_VOLTAGS;
670
671                 if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr)) {
672                         free(cesr.cesr_element_status);
673                         err(1, "%s: CHIOGSTATUS", changer_name);
674                 }
675
676                 /* Dump the status for each reported element. */
677                 for (i = 0; i < count; ++i) {
678                         struct changer_element_status *ces =
679                                  &(cesr.cesr_element_status[i]);
680                         printf("%s %d: %s", description, ces->ces_addr,
681                             bits_to_string(ces->ces_flags,
682                                            CESTATUS_BITS));
683                         if (sense)
684                                 printf(" sense: <0x%02x/0x%02x>",
685                                        ces->ces_sensecode, 
686                                        ces->ces_sensequal);
687                         if (pvoltag)
688                                 printf(" voltag: <%s:%d>", 
689                                        ces->ces_pvoltag.cv_volid,
690                                        ces->ces_pvoltag.cv_serial);
691                         if (avoltag)
692                                 printf(" avoltag: <%s:%d>", 
693                                        ces->ces_avoltag.cv_volid,
694                                        ces->ces_avoltag.cv_serial);
695                         if (source) {
696                                 if (ces->ces_flags & CES_SOURCE_VALID)
697                                         printf(" source: <%s %d>", 
698                                                element_type_name(
699                                                        ces->ces_source_type),
700                                                ces->ces_source_addr);
701                                 else
702                                         printf(" source: <>");
703                         }
704                         if (intaddr)
705                                 printf(" intaddr: <%d>", ces->ces_int_addr);
706                         if (scsi) {
707                                 printf(" scsi: <");
708                                 if (ces->ces_flags & CES_SCSIID_VALID)
709                                         printf("%d", ces->ces_scsi_id);
710                                 else
711                                         putchar('?');
712                                 putchar(':');
713                                 if (ces->ces_flags & CES_LUN_VALID)
714                                         printf("%d", ces->ces_scsi_lun);
715                                 else
716                                         putchar('?');
717                                 putchar('>');
718                         }
719                         if (ces->ces_designator_length > 0)
720                                 print_designator(ces->ces_designator,
721                                                  ces->ces_code_set,
722                                                  ces->ces_designator_length);
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                        getprogname(), 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                        getprogname(), 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 = (u_int16_t)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 = (u_int16_t)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                        getprogname(), cname);
843         return 1;
844 }
845
846 static u_int16_t
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 ((u_int16_t)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 u_int16_t
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 ((u_int16_t)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 (((int)v & (1 << (f - 1))) == 0)
923                         continue;
924                 (void) snprintf(bp, sizeof(buf) - (size_t)(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         uint16_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             CHET_VT == type);
971
972         if (NULL == ces)
973                 errx(1, "%s: null element status pointer", cname);
974
975         if (!(ces->ces_flags & CES_SOURCE_VALID))
976                 errx(1, "%s: no source information", cname);
977
978         (void) memset(&cmd, 0, sizeof(cmd));
979
980         cmd.cm_fromtype = type;
981         cmd.cm_fromunit = element;
982         cmd.cm_totype = ces->ces_source_type;
983         cmd.cm_tounit = ces->ces_source_addr;
984
985         if (ioctl(changer_fd, CHIOMOVE, &cmd) == -1)
986                 err(1, "%s: CHIOMOVE", changer_name);
987         free(ces);
988
989         return(0);
990
991 usage:
992         (void) fprintf(stderr, "usage: %s %s "
993             "<from ET> <from EU>\n", getprogname(), cname);
994         return(1);
995 }
996
997 /*
998  * get_element_status()
999  *
1000  * return a *cesr for the specified changer element.  This
1001  * routing will malloc()/calloc() the memory.  The caller
1002  * should free() it when done.
1003  */
1004 static struct changer_element_status *
1005 get_element_status(unsigned int type, unsigned int element, int use_voltags)
1006 {
1007         struct changer_element_status_request cesr;
1008         struct changer_element_status *ces;
1009         
1010         ces = (struct changer_element_status *)
1011             calloc((size_t)1, sizeof(struct changer_element_status));
1012
1013         if (NULL == ces)
1014                 errx(1, "can't allocate status storage");
1015
1016         (void)memset(&cesr, 0, sizeof(cesr));
1017
1018         cesr.cesr_element_type = (uint16_t)type;
1019         cesr.cesr_element_base = (uint16_t)element;
1020         cesr.cesr_element_count = 1;            /* Only this one element */
1021         if (use_voltags)
1022                 cesr.cesr_flags |= CESR_VOLTAGS; /* Grab voltags as well */
1023         cesr.cesr_element_status = ces;
1024
1025         if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) {
1026                 free(ces);
1027                 err(1, "%s: CHIOGSTATUS", changer_name);
1028                 /* NOTREACHED */
1029         }
1030
1031         return ces;
1032 }
1033
1034
1035 /*
1036  * find_element()
1037  * 
1038  * Given a <voltag> find the chager element and unit, or exit
1039  * with an error if it isn't found.  We grab the changer status
1040  * and iterate until we find a match, or crap out.
1041  */
1042 static void
1043 find_element(char *voltag, uint16_t *et, uint16_t *eu)
1044 {
1045         struct changer_params cp;
1046         struct changer_element_status_request cesr;
1047         struct changer_element_status *ch_ces, *ces;
1048         int found = 0;
1049         size_t elem, total_elem;
1050
1051         /*
1052          * Get the changer parameters, we're interested in the counts
1053          * for all types of elements to perform our search.
1054          */
1055         if (ioctl(changer_fd, CHIOGPARAMS, (char *)&cp))
1056                 err(1, "%s: CHIOGPARAMS", changer_name);
1057
1058         /* Allocate some memory for the results */
1059         total_elem = (cp.cp_nslots + cp.cp_ndrives
1060             + cp.cp_npickers + cp.cp_nportals);
1061         
1062         ch_ces = (struct changer_element_status *)
1063             calloc(total_elem, sizeof(struct changer_element_status));
1064
1065         if (NULL == ch_ces)
1066                 errx(1, "can't allocate status storage");
1067
1068         ces = ch_ces;
1069
1070         /* Read in the changer slots */
1071         if (cp.cp_nslots > 0) {
1072                 (void) memset(&cesr, 0, sizeof(cesr));
1073                 cesr.cesr_element_type = CHET_ST;
1074                 cesr.cesr_element_base = 0;
1075                 cesr.cesr_element_count = cp.cp_nslots;
1076                 cesr.cesr_flags |= CESR_VOLTAGS;
1077                 cesr.cesr_element_status = ces;
1078
1079                 if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) {
1080                         free(ch_ces);
1081                         err(1, "%s: CHIOGSTATUS", changer_name);
1082                 }
1083                 ces += cp.cp_nslots;
1084         }       
1085
1086         /* Read in the drive information */
1087         if (cp.cp_ndrives > 0 ) {
1088
1089                 (void) memset(&cesr, 0, sizeof(cesr));
1090                 cesr.cesr_element_type = CHET_DT;
1091                 cesr.cesr_element_base = 0;
1092                 cesr.cesr_element_count = cp.cp_ndrives;
1093                 cesr.cesr_flags |= CESR_VOLTAGS;
1094                 cesr.cesr_element_status = ces;
1095
1096                 if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) {
1097                         free(ch_ces);
1098                         err(1, "%s: CHIOGSTATUS", changer_name);
1099                 }
1100                 ces += cp.cp_ndrives;
1101         }
1102
1103         /* Read in the portal information */
1104         if (cp.cp_nportals > 0 ) {
1105                 (void) memset(&cesr, 0, sizeof(cesr));
1106                 cesr.cesr_element_type = CHET_IE;
1107                 cesr.cesr_element_base = 0;
1108                 cesr.cesr_element_count = cp.cp_nportals;
1109                 cesr.cesr_flags |= CESR_VOLTAGS;
1110                 cesr.cesr_element_status = ces;
1111
1112                 if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) {
1113                         free(ch_ces);
1114                         err(1, "%s: CHIOGSTATUS", changer_name);
1115                 }
1116                 ces += cp.cp_nportals;
1117         }
1118
1119         /* Read in the picker information */
1120         if (cp.cp_npickers > 0) {
1121                 (void) memset(&cesr, 0, sizeof(cesr));
1122                 cesr.cesr_element_type = CHET_MT;
1123                 cesr.cesr_element_base = 0;
1124                 cesr.cesr_element_count = cp.cp_npickers;
1125                 cesr.cesr_flags |= CESR_VOLTAGS;
1126                 cesr.cesr_element_status = ces;
1127
1128                 if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) {
1129                         free(ch_ces);
1130                         err(1, "%s: CHIOGSTATUS", changer_name);
1131                 }
1132         }
1133
1134         /*
1135          * Now search the list the specified <voltag>
1136          */     
1137         for (elem = 0; elem < total_elem; ++elem) {
1138
1139                 ces = &ch_ces[elem];
1140
1141                 /* Make sure we have a tape in this element */
1142                 if ((ces->ces_flags & (CES_STATUS_ACCESS|CES_STATUS_FULL))
1143                     != (CES_STATUS_ACCESS|CES_STATUS_FULL))
1144                         continue;
1145
1146                 /* Check to see if it is our target */
1147                 if (strcasecmp(voltag,
1148                     (const char *)ces->ces_pvoltag.cv_volid) == 0) {
1149                         *et = ces->ces_type;
1150                         *eu = ces->ces_addr;
1151                         ++found;
1152                         break;
1153                 }
1154         }
1155         if (!found) {
1156                 errx(1, "%s: unable to locate voltag: %s", changer_name,
1157                      voltag);
1158         }
1159         free(ch_ces);
1160         return;
1161 }
1162
1163 static void
1164 cleanup(void)
1165 {
1166         /* Simple enough... */
1167         (void)close(changer_fd);
1168 }
1169
1170 static void
1171 usage(void)
1172 {
1173         (void)fprintf(stderr, "usage: %s [-f changer] command [-<flags>] "
1174                 "arg1 arg2 [arg3 [...]]\n", getprogname());
1175         exit(1);
1176 }
1177
1178 #define UTF8CODESET     "UTF-8"
1179
1180 static void
1181 print_designator(const char *designator, u_int8_t code_set,
1182     u_int8_t designator_length)
1183 {
1184         printf(" serial number: <");
1185         switch (code_set) {
1186         case CES_CODE_SET_ASCII: {
1187                 /*
1188                  * The driver insures that the string is always NUL terminated.
1189                  */
1190                 printf("%s", designator);
1191                 break;
1192         }
1193         case CES_CODE_SET_UTF_8: {
1194                 char *cs_native;
1195
1196                 setlocale(LC_ALL, "");
1197                 cs_native = nl_langinfo(CODESET);
1198
1199                 /* See if we can natively print UTF-8 */
1200                 if (strcmp(cs_native, UTF8CODESET) == 0)
1201                         cs_native = NULL;
1202
1203                 if (cs_native == NULL) {
1204                         /* We can natively print UTF-8, so use printf. */
1205                         printf("%s", designator);
1206                 } else {
1207                         int i;
1208
1209                         /*
1210                          * We can't natively print UTF-8.  We should
1211                          * convert it to the terminal's codeset, but that
1212                          * requires iconv(3) and FreeBSD doesn't have
1213                          * iconv(3) in the base system yet.  So we use %XX
1214                          * notation for non US-ASCII characters instead.
1215                          */
1216                         for (i = 0; i < designator_length &&
1217                             designator[i] != '\0'; i++) {
1218                                 if ((unsigned char)designator[i] < 0x80)
1219                                         printf("%c", designator[i]);
1220                                 else
1221                                         printf("%%%02x",
1222                                             (unsigned char)designator[i]);
1223                         }
1224                 }
1225                 break;
1226         }
1227         case CES_CODE_SET_BINARY: {
1228                 int i;
1229
1230                 for (i = 0; i < designator_length; i++)
1231                         printf("%02X%s", designator[i],
1232                             (i == designator_length - 1) ? "" : " ");
1233                 break;
1234         }
1235         default:
1236                 break;
1237         }
1238         printf(">");
1239 }