chio.c revision 90107
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
39static const char copyright[] =
40	"@(#) Copyright (c) 1996 Jason R. Thorpe.  All rights reserved.";
41static const char rcsid[] =
42  "$FreeBSD: head/bin/chio/chio.c 90107 2002-02-02 06:15:22Z imp $";
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
57extern	char *__progname;	/* from crt0.o */
58
59static	void usage(void);
60static	void cleanup(void);
61static	int parse_element_type(char *);
62static	int parse_element_unit(char *);
63static	const char * element_type_name(int et);
64static	int parse_special(char *);
65static	int is_special(char *);
66static	const char *bits_to_string(ces_status_flags, const char *);
67
68static	void find_element(char *, u_int16_t *, u_int16_t *);
69static	struct changer_element_status *get_element_status
70	   (unsigned int, unsigned int);
71
72static	int do_move(const char *, int, char **);
73static	int do_exchange(const char *, int, char **);
74static	int do_position(const char *, int, char **);
75static	int do_params(const char *, int, char **);
76static	int do_getpicker(const char *, int, char **);
77static	int do_setpicker(const char *, int, char **);
78static	int do_status(const char *, int, char **);
79static	int do_ielem(const char *, int, char **);
80static	int do_return(const char *, int, char **);
81static	int do_voltag(const char *, int, char **);
82
83#ifndef CHET_VT
84#define	CHET_VT		10			/* Completely Arbitrary */
85#endif
86
87/* Valid changer element types. */
88const 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. */
98const 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. */
113const struct special_word specials[] = {
114	{ "inv",		SW_INVERT },
115	{ "inv1",		SW_INVERT1 },
116	{ "inv2",		SW_INVERT2 },
117	{ NULL,			0 },
118};
119
120static	int changer_fd;
121static	const char *changer_name;
122
123int
124main(int argc, char **argv)
125{
126	int ch, i;
127
128	while ((ch = getopt(argc, argv, "f:")) != -1) {
129		switch (ch) {
130		case 'f':
131			changer_name = optarg;
132			break;
133
134		default:
135			usage();
136		}
137	}
138	argc -= optind;
139	argv += optind;
140
141	if (argc == 0)
142		usage();
143
144	/* Get the default changer if not already specified. */
145	if (changer_name == NULL)
146		if ((changer_name = getenv(CHANGER_ENV_VAR)) == NULL)
147			changer_name = _PATH_CH;
148
149	/* Open the changer device. */
150	if ((changer_fd = open(changer_name, O_RDWR, 0600)) == -1)
151		err(1, "%s: open", changer_name);
152
153	/* Register cleanup function. */
154	if (atexit(cleanup))
155		err(1, "can't register cleanup function");
156
157	/* Find the specified command. */
158	for (i = 0; commands[i].cc_name != NULL; ++i)
159		if (strcmp(*argv, commands[i].cc_name) == 0)
160			break;
161	if (commands[i].cc_name == NULL) {
162		/* look for abbreviation */
163		for (i = 0; commands[i].cc_name != NULL; ++i)
164			if (strncmp(*argv, commands[i].cc_name,
165			    strlen(*argv)) == 0)
166				break;
167	}
168
169	if (commands[i].cc_name == NULL)
170		errx(1, "unknown command: %s", *argv);
171
172	exit ((*commands[i].cc_handler)(commands[i].cc_name, argc, argv));
173	/* NOTREACHED */
174}
175
176static int
177do_move(const char *cname, int argc, char **argv)
178{
179	struct changer_move cmd;
180	int val;
181
182	/*
183	 * On a move command, we expect the following:
184	 *
185	 * <from ET> <from EU> <to ET> <to EU> [inv]
186	 *
187	 * where ET == element type and EU == element unit.
188	 */
189
190	++argv; --argc;
191
192	if (argc < 4) {
193		warnx("%s: too few arguments", cname);
194		goto usage;
195	} else if (argc > 5) {
196		warnx("%s: too many arguments", cname);
197		goto usage;
198	}
199	(void) memset(&cmd, 0, sizeof(cmd));
200
201	/* <from ET>  */
202	cmd.cm_fromtype = parse_element_type(*argv);
203	++argv; --argc;
204
205	/* Check for voltag virtual type */
206	if (CHET_VT == cmd.cm_fromtype) {
207		find_element(*argv, &cmd.cm_fromtype, &cmd.cm_fromunit);
208	} else {
209		/* <from EU> */
210		cmd.cm_fromunit = parse_element_unit(*argv);
211	}
212	++argv; --argc;
213
214	/* <to ET> */
215	cmd.cm_totype = parse_element_type(*argv);
216	++argv; --argc;
217
218	/* Check for voltag virtual type, and report error */
219	if (CHET_VT == cmd.cm_totype)
220		errx(1,"%s: voltag only makes sense as an element source",
221		     cname);
222
223	/* <to EU> */
224	cmd.cm_tounit = parse_element_unit(*argv);
225	++argv; --argc;
226
227	/* Deal with optional command modifier. */
228	if (argc) {
229		val = parse_special(*argv);
230		switch (val) {
231		case SW_INVERT:
232			cmd.cm_flags |= CM_INVERT;
233			break;
234
235		default:
236			errx(1, "%s: inappropriate modifier `%s'",
237			    cname, *argv);
238			/* NOTREACHED */
239		}
240	}
241
242	/* Send command to changer. */
243	if (ioctl(changer_fd, CHIOMOVE, &cmd))
244		err(1, "%s: CHIOMOVE", changer_name);
245
246	return (0);
247
248 usage:
249	(void) fprintf(stderr, "usage: %s %s "
250	    "<from ET> <from EU> <to ET> <to EU> [inv]\n", __progname, cname);
251	return (1);
252}
253
254static int
255do_exchange(const char *cname, int argc, char **argv)
256{
257	struct changer_exchange cmd;
258	int val;
259
260	/*
261	 * On an exchange command, we expect the following:
262	 *
263  * <src ET> <src EU> <dst1 ET> <dst1 EU> [<dst2 ET> <dst2 EU>] [inv1] [inv2]
264	 *
265	 * where ET == element type and EU == element unit.
266	 */
267
268	++argv; --argc;
269
270	if (argc < 4) {
271		warnx("%s: too few arguments", cname);
272		goto usage;
273	} else if (argc > 8) {
274		warnx("%s: too many arguments", cname);
275		goto usage;
276	}
277	(void) memset(&cmd, 0, sizeof(cmd));
278
279	/* <src ET>  */
280	cmd.ce_srctype = parse_element_type(*argv);
281	++argv; --argc;
282
283	/* Check for voltag virtual type */
284	if (CHET_VT == cmd.ce_srctype) {
285		find_element(*argv, &cmd.ce_srctype, &cmd.ce_srcunit);
286	} else {
287		/* <from EU> */
288		cmd.ce_srcunit = parse_element_unit(*argv);
289	}
290	++argv; --argc;
291
292	/* <dst1 ET> */
293	cmd.ce_fdsttype = parse_element_type(*argv);
294	++argv; --argc;
295
296	/* Check for voltag virtual type */
297	if (CHET_VT == cmd.ce_fdsttype) {
298		find_element(*argv, &cmd.ce_fdsttype, &cmd.ce_fdstunit);
299	} else {
300		/* <from EU> */
301		cmd.ce_fdstunit = parse_element_unit(*argv);
302	}
303	++argv; --argc;
304
305	/*
306	 * If the next token is a special word or there are no more
307	 * arguments, then this is a case of simple exchange.
308	 * dst2 == src.
309	 */
310	if ((argc == 0) || is_special(*argv)) {
311		cmd.ce_sdsttype = cmd.ce_srctype;
312		cmd.ce_sdstunit = cmd.ce_srcunit;
313		goto do_special;
314	}
315
316	/* <dst2 ET> */
317	cmd.ce_sdsttype = parse_element_type(*argv);
318	++argv; --argc;
319
320	if (CHET_VT == cmd.ce_sdsttype)
321		errx(1,"%s %s: voltag only makes sense as an element source",
322		     cname, *argv);
323
324	/* <dst2 EU> */
325	cmd.ce_sdstunit = parse_element_unit(*argv);
326	++argv; --argc;
327
328 do_special:
329	/* Deal with optional command modifiers. */
330	while (argc) {
331		val = parse_special(*argv);
332		++argv; --argc;
333		switch (val) {
334		case SW_INVERT1:
335			cmd.ce_flags |= CE_INVERT1;
336			break;
337
338		case SW_INVERT2:
339			cmd.ce_flags |= CE_INVERT2;
340			break;
341
342		default:
343			errx(1, "%s: inappropriate modifier `%s'",
344			    cname, *argv);
345			/* NOTREACHED */
346		}
347	}
348
349	/* Send command to changer. */
350	if (ioctl(changer_fd, CHIOEXCHANGE, &cmd))
351		err(1, "%s: CHIOEXCHANGE", changer_name);
352
353	return (0);
354
355 usage:
356	(void) fprintf(stderr,
357	    "usage: %s %s <src ET> <src EU> <dst1 ET> <dst1 EU>\n"
358	    "       [<dst2 ET> <dst2 EU>] [inv1] [inv2]\n",
359	    __progname, cname);
360	return (1);
361}
362
363static int
364do_position(const char *cname, int argc, char **argv)
365{
366	struct changer_position cmd;
367	int val;
368
369	/*
370	 * On a position command, we expect the following:
371	 *
372	 * <to ET> <to EU> [inv]
373	 *
374	 * where ET == element type and EU == element unit.
375	 */
376
377	++argv; --argc;
378
379	if (argc < 2) {
380		warnx("%s: too few arguments", cname);
381		goto usage;
382	} else if (argc > 3) {
383		warnx("%s: too many arguments", cname);
384		goto usage;
385	}
386	(void) memset(&cmd, 0, sizeof(cmd));
387
388	/* <to ET>  */
389	cmd.cp_type = parse_element_type(*argv);
390	++argv; --argc;
391
392	/* <to EU> */
393	cmd.cp_unit = parse_element_unit(*argv);
394	++argv; --argc;
395
396	/* Deal with optional command modifier. */
397	if (argc) {
398		val = parse_special(*argv);
399		switch (val) {
400		case SW_INVERT:
401			cmd.cp_flags |= CP_INVERT;
402			break;
403
404		default:
405			errx(1, "%s: inappropriate modifier `%s'",
406			    cname, *argv);
407			/* NOTREACHED */
408		}
409	}
410
411	/* Send command to changer. */
412	if (ioctl(changer_fd, CHIOPOSITION, &cmd))
413		err(1, "%s: CHIOPOSITION", changer_name);
414
415	return (0);
416
417 usage:
418	(void) fprintf(stderr, "usage: %s %s <to ET> <to EU> [inv]\n",
419	    __progname, cname);
420	return (1);
421}
422
423/* ARGSUSED */
424static int
425do_params(const char *cname, int argc, char **argv)
426{
427	struct changer_params data;
428	int picker;
429
430	/* No arguments to this command. */
431
432	++argv; --argc;
433
434	if (argc) {
435		warnx("%s: no arguments expected", cname);
436		goto usage;
437	}
438
439	/* Get params from changer and display them. */
440	(void) memset(&data, 0, sizeof(data));
441	if (ioctl(changer_fd, CHIOGPARAMS, &data))
442		err(1, "%s: CHIOGPARAMS", changer_name);
443
444	(void) printf("%s: %d slot%s, %d drive%s, %d picker%s",
445	    changer_name,
446	    data.cp_nslots, (data.cp_nslots > 1) ? "s" : "",
447	    data.cp_ndrives, (data.cp_ndrives > 1) ? "s" : "",
448	    data.cp_npickers, (data.cp_npickers > 1) ? "s" : "");
449	if (data.cp_nportals)
450		(void) printf(", %d portal%s", data.cp_nportals,
451		    (data.cp_nportals > 1) ? "s" : "");
452
453	/* Get current picker from changer and display it. */
454	if (ioctl(changer_fd, CHIOGPICKER, &picker))
455		err(1, "%s: CHIOGPICKER", changer_name);
456
457	(void) printf("\n%s: current picker: %d\n", changer_name, picker);
458
459	return (0);
460
461 usage:
462	(void) fprintf(stderr, "usage: %s %s\n", __progname, cname);
463	return (1);
464}
465
466/* ARGSUSED */
467static int
468do_getpicker(const char *cname, int argc, char **argv)
469{
470	int picker;
471
472	/* No arguments to this command. */
473
474	++argv; --argc;
475
476	if (argc) {
477		warnx("%s: no arguments expected", cname);
478		goto usage;
479	}
480
481	/* Get current picker from changer and display it. */
482	if (ioctl(changer_fd, CHIOGPICKER, &picker))
483		err(1, "%s: CHIOGPICKER", changer_name);
484
485	(void) printf("%s: current picker: %d\n", changer_name, picker);
486
487	return (0);
488
489 usage:
490	(void) fprintf(stderr, "usage: %s %s\n", __progname, cname);
491	return (1);
492}
493
494static int
495do_setpicker(const char *cname, int argc, char **argv)
496{
497	int picker;
498
499	++argv; --argc;
500
501	if (argc < 1) {
502		warnx("%s: too few arguments", cname);
503		goto usage;
504	} else if (argc > 1) {
505		warnx("%s: too many arguments", cname);
506		goto usage;
507	}
508
509	picker = parse_element_unit(*argv);
510
511	/* Set the changer picker. */
512	if (ioctl(changer_fd, CHIOSPICKER, &picker))
513		err(1, "%s: CHIOSPICKER", changer_name);
514
515	return (0);
516
517 usage:
518	(void) fprintf(stderr, "usage: %s %s <picker>\n", __progname, cname);
519	return (1);
520}
521
522static int
523do_status(const char *cname, int argc, char **argv)
524{
525	struct changer_params cp;
526	struct changer_element_status_request cesr;
527	int i, count, base, chet, schet, echet;
528	const char *description;
529	int pvoltag = 0;
530	int avoltag = 0;
531	int sense = 0;
532	int scsi = 0;
533	int source = 0;
534	int intaddr = 0;
535	int c;
536
537	count = 0;
538	base = 0;
539	description = NULL;
540
541	optind = optreset = 1;
542	while ((c = getopt(argc, argv, "vVsSbaI")) != -1) {
543		switch (c) {
544		case 'v':
545			pvoltag = 1;
546			break;
547		case 'V':
548			avoltag = 1;
549			break;
550		case 's':
551			sense = 1;
552			break;
553		case 'S':
554			source = 1;
555			break;
556		case 'b':
557			scsi = 1;
558			break;
559		case 'I':
560			intaddr = 1;
561			break;
562		case 'a':
563			pvoltag = avoltag = source = sense = scsi = intaddr = 1;
564			break;
565		default:
566			warnx("%s: bad option", cname);
567			goto usage;
568		}
569	}
570
571	argc -= optind;
572	argv += optind;
573
574	/*
575	 * On a status command, we expect the following:
576	 *
577	 * [<ET> [<start> [<end>] ] ]
578	 *
579	 * where ET == element type, start == first element to report,
580	 * end == number of elements to report
581	 *
582	 * If we get no arguments, we get the status of all
583	 * known element types.
584	 */
585	if (argc > 3) {
586		warnx("%s: too many arguments", cname);
587		goto usage;
588	}
589
590	/*
591	 * Get params from changer.  Specifically, we need the element
592	 * counts.
593	 */
594	if (ioctl(changer_fd, CHIOGPARAMS, (char *)&cp))
595		err(1, "%s: CHIOGPARAMS", changer_name);
596
597	if (argc > 0)
598		schet = echet = parse_element_type(argv[0]);
599	else {
600		schet = CHET_MT;
601		echet = CHET_DT;
602	}
603	if (argc > 1) {
604		base = atol(argv[1]);
605		count = 1;
606	}
607	if (argc > 2)
608		count = atol(argv[2]) - base + 1;
609
610	if (base < 0 || count < 0)
611		errx(1, "bad arguments");
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		       __progname, 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		       __progname, 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 = 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 = 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		       __progname, cname);
846	return 1;
847}
848
849static int
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 (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 int
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 (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 ((v & (1 << (f - 1))) == 0)
926			continue;
927		(void) snprintf(bp, sizeof(buf) - (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	u_int16_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", __progname, 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 = (u_int16_t)type;
1021	cesr.cesr_element_base = (u_int16_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, u_int16_t *et, u_int16_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", __progname);
1175	exit(1);
1176}
1177