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