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