Sync our rm -P option with OpenBSD - if the file has a hardlink count
[dragonfly.git] / bin / chio / chio.c
1 /*
2  * Copyright (c) 1996 Jason R. Thorpe <thorpej@and.com>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgements:
15  *      This product includes software developed by Jason R. Thorpe
16  *      for And Communications, http://www.and.com/
17  * 4. The name of the author may not be used to endorse or promote products
18  *    derived from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * @(#) Copyright (c) 1996 Jason R. Thorpe.  All rights reserved.
33  * $NetBSD: chio.c,v 1.6 1998/01/04 23:53:58 thorpej Exp $
34  * $FreeBSD: src/bin/chio/chio.c,v 1.15.2.3 2001/07/28 19:22:01 mikeh Exp $
35  * $DragonFly: src/bin/chio/chio.c,v 1.5 2004/08/25 01:13:05 dillon 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/chio.h> 
43 #include <sys/param.h>
44
45 #include <err.h>
46 #include <fcntl.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <unistd.h>
51
52 #include "defs.h"
53 #include "pathnames.h"
54
55 extern  char *__progname;       /* from crt0.o */
56
57 static  void usage (void);
58 static  void cleanup (void);
59 static  int parse_element_type (char *);
60 static  int parse_element_unit (char *);
61 static  const char * element_type_name (int et);
62 static  int parse_special (char *);
63 static  int is_special (char *);
64 static  const char *bits_to_string (ces_status_flags, const char *);
65
66 static  void find_element (char *, u_int16_t *, u_int16_t *);
67 static  struct changer_element_status *get_element_status
68             (unsigned int, unsigned int);
69
70 static  int do_move (const char *, int, char **);
71 static  int do_exchange (const char *, int, char **);
72 static  int do_position (const char *, int, char **);
73 static  int do_params (const char *, int, char **);
74 static  int do_getpicker (const char *, int, char **);
75 static  int do_setpicker (const char *, int, char **);
76 static  int do_status (const char *, int, char **);
77 static  int do_ielem (const char *, int, char **);
78 static  int do_return (const char *, int, char **);
79 static  int do_voltag (const char *, int, char **);
80
81 #ifndef CHET_VT
82 #define CHET_VT         10                      /* Completely Arbitrary */
83 #endif
84
85 /* Valid changer element types. */
86 const struct element_type elements[] = {
87         { "drive",              CHET_DT },
88         { "picker",             CHET_MT },
89         { "portal",             CHET_IE },
90         { "slot",               CHET_ST },
91         { "voltag",             CHET_VT },      /* Select tapes by barcode */
92         { NULL,                 0 },
93 };
94
95 /* Valid commands. */
96 const struct changer_command commands[] = {
97         { "exchange",           do_exchange },
98         { "getpicker",          do_getpicker },
99         { "ielem",              do_ielem },
100         { "move",               do_move },
101         { "params",             do_params },
102         { "position",           do_position },
103         { "setpicker",          do_setpicker },
104         { "status",             do_status },
105         { "return",             do_return },
106         { "voltag",             do_voltag },
107         { NULL,                 0 },
108 };
109
110 /* Valid special words. */
111 const struct special_word specials[] = {
112         { "inv",                SW_INVERT },
113         { "inv1",               SW_INVERT1 },
114         { "inv2",               SW_INVERT2 },
115         { NULL,                 0 },
116 };
117
118 static  int changer_fd;
119 static  const char *changer_name;
120
121 int
122 main(int argc, char **argv)
123 {
124         int ch, i;
125
126         while ((ch = getopt(argc, argv, "f:")) != -1) {
127                 switch (ch) {
128                 case 'f':
129                         changer_name = optarg;
130                         break;
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         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         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         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         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         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         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         memset(&data, 0, sizeof(data));
438         if (ioctl(changer_fd, CHIOGPARAMS, &data))
439                 err(1, "%s: CHIOGPARAMS", changer_name);
440
441         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                 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         printf("\n%s: current picker: %d\n", changer_name, picker);
455
456         return (0);
457
458  usage:
459         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         printf("%s: current picker: %d\n", changer_name, picker);
483
484         return (0);
485
486  usage:
487         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         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         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         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         fprintf(stderr, "usage: %s %s [-fca] <element> [<voltag> [<vsn>] ]\n",
841                        __progname, cname);
842         return 1;
843 }
844
845 static int
846 parse_element_type(char *cp)
847 {
848         int i;
849
850         for (i = 0; elements[i].et_name != NULL; ++i)
851                 if (strcmp(elements[i].et_name, cp) == 0)
852                         return (elements[i].et_type);
853
854         errx(1, "invalid element type `%s'", cp);
855         /* NOTREACHED */
856 }
857
858 static const char *
859 element_type_name(int et)
860 {
861         int i;
862
863         for (i = 0; elements[i].et_name != NULL; i++)
864                 if (elements[i].et_type == et)
865                         return elements[i].et_name;
866
867         return "unknown";
868 }
869
870 static int
871 parse_element_unit(char *cp)
872 {
873         int i;
874         char *p;
875
876         i = (int)strtol(cp, &p, 10);
877         if ((i < 0) || (*p != '\0'))
878                 errx(1, "invalid unit number `%s'", cp);
879
880         return (i);
881 }
882
883 static int
884 parse_special(char *cp)
885 {
886         int val;
887
888         val = is_special(cp);
889         if (val)
890                 return (val);
891
892         errx(1, "invalid modifier `%s'", cp);
893         /* NOTREACHED */
894 }
895
896 static int
897 is_special(char *cp)
898 {
899         int i;
900
901         for (i = 0; specials[i].sw_name != NULL; ++i)
902                 if (strcmp(specials[i].sw_name, cp) == 0)
903                         return (specials[i].sw_value);
904
905         return (0);
906 }
907
908 static const char *
909 bits_to_string(ces_status_flags v, const char *cp)
910 {
911         const char *np;
912         char f, sep, *bp;
913         static char buf[128];
914
915         bp = buf;
916         memset(buf, 0, sizeof(buf));
917
918         for (sep = '<'; (f = *cp++) != 0; cp = np) {
919                 for (np = cp; *np >= ' ';)
920                         np++;
921                 if ((v & (1 << (f - 1))) == 0)
922                         continue;
923                 snprintf(bp, sizeof(buf) - (bp - &buf[0]),
924                         "%c%.*s", sep, (int)(long)(np - cp), cp);
925                 bp += strlen(bp);
926                 sep = ',';
927         }
928         if (sep != '<')
929                 *bp = '>';
930
931         return (buf);
932 }
933 /*
934  * do_return()
935  * 
936  * Given an element reference, ask the changer/picker to move that
937  * element back to its source slot.
938  */
939 static int
940 do_return(const char *cname, int  argc, char **argv)
941 {
942         struct changer_element_status *ces;
943         struct changer_move cmd;
944         u_int16_t       type, element;
945
946         ++argv; --argc;
947
948         if (argc < 2) {
949                 warnx("%s: too few arguments", cname);
950                 goto usage;
951         } else if (argc > 3) {
952                 warnx("%s: too many arguments", cname);
953                 goto usage;
954         }
955
956         type = parse_element_type(*argv);
957         ++argv; --argc;
958         
959         /* Handle voltag virtual Changer Element Type */
960         if (CHET_VT == type) {
961                 find_element(*argv, &type, &element);
962         } else {
963                 element = parse_element_unit(*argv);
964         }
965         ++argv; --argc;
966
967         /* Get the status */
968         ces = get_element_status((unsigned int)type, (unsigned int)element);
969
970         if (NULL == ces)
971                 errx(1, "%s: null element status pointer", cname);
972
973         if (!(ces->ces_flags & CES_SOURCE_VALID))
974                 errx(1, "%s: no source information", cname);
975
976         memset(&cmd, 0, sizeof(cmd));
977
978         cmd.cm_fromtype = type;
979         cmd.cm_fromunit = element;
980         cmd.cm_totype = ces->ces_source_type;
981         cmd.cm_tounit = ces->ces_source_addr;
982
983         if (ioctl(changer_fd, CHIOMOVE, &cmd) == -1)
984                 err(1, "%s: CHIOMOVE", changer_name);
985         free(ces);
986
987         return(0);
988
989 usage:
990         fprintf(stderr, "usage: %s %s "
991             "<from ET> <from EU>\n", __progname, cname);
992         return(1);
993 }
994
995 /*
996  * get_element_status()
997  *
998  * return a *cesr for the specified changer element.  This
999  * routing will malloc()/calloc() the memory.  The caller
1000  * should free() it when done.
1001  */
1002 static struct changer_element_status *
1003 get_element_status(unsigned int type, unsigned int element)
1004 {
1005         struct changer_element_status_request cesr;
1006         struct changer_element_status *ces;
1007         
1008         ces = (struct changer_element_status *)
1009             calloc((size_t)1, sizeof(struct changer_element_status));
1010
1011         if (NULL == ces)
1012                 errx(1, "can't allocate status storage");
1013
1014         memset(&cesr, 0, sizeof(cesr));
1015
1016         cesr.cesr_element_type = (u_int16_t)type;
1017         cesr.cesr_element_base = (u_int16_t)element;
1018         cesr.cesr_element_count = 1;            /* Only this one element */
1019         cesr.cesr_flags |= CESR_VOLTAGS;        /* Grab voltags as well */
1020         cesr.cesr_element_status = ces;
1021
1022         if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) {
1023                 free(ces);
1024                 err(1, "%s: CHIOGSTATUS", changer_name);
1025                 /* NOTREACHED */
1026         }
1027
1028         return ces;
1029 }
1030
1031
1032 /*
1033  * find_element()
1034  * 
1035  * Given a <voltag> find the chager element and unit, or exit
1036  * with an error if it isn't found.  We grab the changer status
1037  * and iterate until we find a match, or crap out.
1038  */
1039 static void
1040 find_element(char *voltag, u_int16_t *et, u_int16_t *eu)
1041 {
1042         struct changer_params cp;
1043         struct changer_element_status_request cesr;
1044         struct changer_element_status *ch_ces, *ces;
1045         int found = 0;
1046         size_t elem, total_elem;
1047
1048         /*
1049          * Get the changer parameters, we're interested in the counts
1050          * for all types of elements to perform our search.
1051          */
1052         if (ioctl(changer_fd, CHIOGPARAMS, (char *)&cp))
1053                 err(1, "%s: CHIOGPARAMS", changer_name);
1054
1055         /* Allocate some memory for the results */
1056         total_elem = (cp.cp_nslots + cp.cp_ndrives
1057             + cp.cp_npickers + cp.cp_nportals);
1058         
1059         ch_ces = (struct changer_element_status *)
1060             calloc(total_elem, sizeof(struct changer_element_status));
1061
1062         if (NULL == ch_ces)
1063                 errx(1, "can't allocate status storage");
1064
1065         ces = ch_ces;
1066
1067         /* Read in the changer slots */
1068         if (cp.cp_nslots > 0) {
1069                 cesr.cesr_element_type = CHET_ST;
1070                 cesr.cesr_element_base = 0;
1071                 cesr.cesr_element_count = cp.cp_nslots;
1072                 cesr.cesr_flags |= CESR_VOLTAGS;
1073                 cesr.cesr_element_status = ces;
1074
1075                 if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) {
1076                         free(ch_ces);
1077                         err(1, "%s: CHIOGSTATUS", changer_name);
1078                 }
1079                 ces += cp.cp_nslots;
1080         }       
1081
1082         /* Read in the drive information */
1083         if (cp.cp_ndrives > 0 ) {
1084
1085                 memset(&cesr, 0, sizeof(cesr));
1086                 cesr.cesr_element_type = CHET_DT;
1087                 cesr.cesr_element_base = 0;
1088                 cesr.cesr_element_count = cp.cp_ndrives;
1089                 cesr.cesr_flags |= CESR_VOLTAGS;
1090                 cesr.cesr_element_status = ces;
1091
1092                 if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) {
1093                         free(ch_ces);
1094                         err(1, "%s: CHIOGSTATUS", changer_name);
1095                 }
1096                 ces += cp.cp_ndrives;
1097         }
1098
1099         /* Read in the portal information */
1100         if (cp.cp_nportals > 0 ) {
1101                 memset(&cesr, 0, sizeof(cesr));
1102                 cesr.cesr_element_type = CHET_IE;
1103                 cesr.cesr_element_base = 0;
1104                 cesr.cesr_element_count = cp.cp_nportals;
1105                 cesr.cesr_flags |= CESR_VOLTAGS;
1106                 cesr.cesr_element_status = ces;
1107
1108                 if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) {
1109                         free(ch_ces);
1110                         err(1, "%s: CHIOGSTATUS", changer_name);
1111                 }
1112                 ces += cp.cp_nportals;
1113         }
1114
1115         /* Read in the picker information */
1116         if (cp.cp_npickers > 0) {
1117                 memset(&cesr, 0, sizeof(cesr));
1118                 cesr.cesr_element_type = CHET_MT;
1119                 cesr.cesr_element_base = 0;
1120                 cesr.cesr_element_count = cp.cp_npickers;
1121                 cesr.cesr_flags |= CESR_VOLTAGS;
1122                 cesr.cesr_element_status = ces;
1123
1124                 if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) {
1125                         free(ch_ces);
1126                         err(1, "%s: CHIOGSTATUS", changer_name);
1127                 }
1128         }
1129
1130         /*
1131          * Now search the list the specified <voltag>
1132          */     
1133         for (elem = 0; elem <= total_elem; ++elem) {
1134
1135                 ces = &ch_ces[elem];
1136
1137                 /* Make sure we have a tape in this element */
1138                 if ((ces->ces_flags & (CES_STATUS_ACCESS|CES_STATUS_FULL))
1139                     != (CES_STATUS_ACCESS|CES_STATUS_FULL))
1140                         continue;
1141
1142                 /* Check to see if it is our target */
1143                 if (strcasecmp(voltag,
1144                     (const char *)ces->ces_pvoltag.cv_volid) == 0) {
1145                         *et = ces->ces_type;
1146                         *eu = ces->ces_addr;
1147                         ++found;
1148                         break;
1149                 }
1150         }
1151         if (!found) {
1152                 errx(1, "%s: unable to locate voltag: %s", changer_name,
1153                      voltag);
1154         }
1155         free(ch_ces);
1156         return;
1157 }
1158
1159 static void
1160 cleanup(void)
1161 {
1162         /* Simple enough... */
1163         close(changer_fd);
1164 }
1165
1166 static void
1167 usage(void)
1168 {
1169         fprintf(stderr, "usage: %s [-f changer] command [-<flags>] "
1170                 "arg1 arg2 [arg3 [...]]\n", __progname);
1171         exit(1);
1172 }