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