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