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