xref: /openbsd/bin/chio/chio.c (revision dcc5d0ad)
1 /*	$OpenBSD: chio.c,v 1.30 2022/10/11 03:37:14 jsg Exp $	*/
2 /*	$NetBSD: chio.c,v 1.1.1.1 1996/04/03 00:34:38 thorpej Exp $	*/
3 
4 /*
5  * Copyright (c) 1996 Jason R. Thorpe <thorpej@and.com>
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgments:
18  *	This product includes software developed by Jason R. Thorpe
19  *	for And Communications, http://www.and.com/
20  * 4. The name of the author may not be used to endorse or promote products
21  *    derived from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <sys/types.h>
37 #include <sys/ioctl.h>
38 #include <sys/mtio.h>
39 #include <sys/chio.h>
40 #include <err.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <limits.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <unistd.h>
48 #include <util.h>
49 
50 #include "defs.h"
51 #include "pathnames.h"
52 
53 #define _PATH_CH_CONF	"/etc/chio.conf"
54 extern	char *parse_tapedev(const char *, const char *, int); /* parse.y */
55 extern	char *__progname;	/* from crt0.o */
56 
57 static	void usage(void);
58 static	int parse_element_type(char *);
59 static	int parse_element_unit(char *);
60 static	int parse_special(char *);
61 static	int is_special(char *);
62 static	const char * element_type_name(int et);
63 static	char *bits_to_string(int, const char *);
64 static	void find_voltag(char *, int *, int *);
65 static	void check_source_drive(int);
66 
67 static	int do_move(char *, int, char **);
68 static	int do_exchange(char *, int, char **);
69 static	int do_position(char *, int, char **);
70 static	int do_params(char *, int, char **);
71 static	int do_getpicker(char *, int, char **);
72 static	int do_setpicker(char *, int, char **);
73 static	int do_status(char *, int, char **);
74 
75 /* Valid changer element types. */
76 const struct element_type elements[] = {
77 	{ "drive",		CHET_DT },
78 	{ "picker",		CHET_MT },
79 	{ "portal",		CHET_IE },
80 	{ "slot",		CHET_ST },
81 	{ NULL,			0 },
82 };
83 
84 /* Valid commands. */
85 const struct changer_command commands[] = {
86 	{ "exchange",		do_exchange },
87 	{ "getpicker",		do_getpicker },
88 	{ "move",		do_move },
89 	{ "params",		do_params },
90 	{ "position",		do_position },
91 	{ "setpicker",		do_setpicker },
92 	{ "status",		do_status },
93 	{ NULL,			0 },
94 };
95 
96 /* Valid special words. */
97 const struct special_word specials[] = {
98 	{ "inv",		SW_INVERT },
99 	{ "inv1",		SW_INVERT1 },
100 	{ "inv2",		SW_INVERT2 },
101 	{ NULL,			0 },
102 };
103 
104 static	int changer_fd;
105 static	char *changer_name;
106 static int avoltag;
107 static int pvoltag;
108 static int sense;
109 static int source;
110 
111 int
main(int argc,char * argv[])112 main(int argc, char *argv[])
113 {
114 	int ch, i;
115 
116 	while ((ch = getopt(argc, argv, "f:")) != -1) {
117 		switch (ch) {
118 		case 'f':
119 			changer_name = optarg;
120 			break;
121 		default:
122 			usage();
123 		}
124 	}
125 	argc -= optind;
126 	argv += optind;
127 
128 	if (argc == 0)
129 		usage();
130 
131 	/* Get the default changer if not already specified. */
132 	if (changer_name == NULL)
133 		if ((changer_name = getenv(CHANGER_ENV_VAR)) == NULL)
134 			changer_name = _PATH_CH;
135 
136 	/* Open the changer device. */
137 	if ((changer_fd = open(changer_name, O_RDWR)) == -1)
138 		err(1, "%s: open", changer_name);
139 
140 	/* Find the specified command. */
141 	for (i = 0; commands[i].cc_name != NULL; ++i)
142 		if (strcmp(*argv, commands[i].cc_name) == 0)
143 			break;
144 	if (commands[i].cc_name == NULL) {
145 		/* look for abbreviation */
146 		for (i = 0; commands[i].cc_name != NULL; ++i)
147 			if (strncmp(*argv, commands[i].cc_name,
148 			    strlen(*argv)) == 0)
149 				break;
150 	}
151 	if (commands[i].cc_name == NULL)
152 		errx(1, "unknown command: %s", *argv);
153 
154 	exit((*commands[i].cc_handler)(commands[i].cc_name, argc, argv));
155 }
156 
157 static int
do_move(char * cname,int argc,char * argv[])158 do_move(char *cname, int argc, char *argv[])
159 {
160 	struct changer_move cmd;
161 	int val;
162 
163 	/*
164 	 * On a move command, we expect the following:
165 	 *
166 	 * <from ET> <from EU> <to ET> <to EU> [inv]
167 	 *
168 	 * where ET == element type and EU == element unit.
169 	 */
170 
171 	++argv; --argc;
172 
173 	if (argc < 4) {
174 		warnx("%s: too few arguments", cname);
175 		goto usage;
176 	} else if (argc > 5) {
177 		warnx("%s: too many arguments", cname);
178 		goto usage;
179 	}
180 	bzero(&cmd, sizeof(cmd));
181 
182 	/*
183 	 * Get the from ET and EU - we search for it if the ET is
184 	 * "voltag", otherwise, we just use the ET and EU given to us.
185 	 */
186 	if (strcmp(*argv, "voltag") == 0) {
187 		++argv; --argc;
188 		find_voltag(*argv, &cmd.cm_fromtype, &cmd.cm_fromunit);
189 		++argv; --argc;
190 	} else {
191 		cmd.cm_fromtype = parse_element_type(*argv);
192 		++argv; --argc;
193 		cmd.cm_fromunit = parse_element_unit(*argv);
194 		++argv; --argc;
195 	}
196 
197 	if (cmd.cm_fromtype == CHET_DT)
198 		check_source_drive(cmd.cm_fromunit);
199 
200 	/*
201 	 * Don't allow voltag on the to ET, using a volume
202 	 * as a destination makes no sense on a move
203 	 */
204 	cmd.cm_totype = parse_element_type(*argv);
205 	++argv; --argc;
206 	cmd.cm_tounit = parse_element_unit(*argv);
207 	++argv; --argc;
208 
209 	/* Deal with optional command modifier. */
210 	if (argc) {
211 		val = parse_special(*argv);
212 		switch (val) {
213 		case SW_INVERT:
214 			cmd.cm_flags |= CM_INVERT;
215 			break;
216 
217 		default:
218 			errx(1, "%s: inappropriate modifier `%s'",
219 			    cname, *argv);
220 			/* NOTREACHED */
221 		}
222 	}
223 
224 	/* Send command to changer. */
225 	if (ioctl(changer_fd, CHIOMOVE, &cmd) == -1)
226 		err(1, "%s: CHIOMOVE", changer_name);
227 
228 	return (0);
229 
230  usage:
231 	fprintf(stderr, "usage: %s %s "
232 	    "<from ET> <from EU> <to ET> <to EU> [inv]\n", __progname, cname);
233 	return (1);
234 }
235 
236 static int
do_exchange(char * cname,int argc,char * argv[])237 do_exchange(char *cname, int argc, char *argv[])
238 {
239 	struct changer_exchange cmd;
240 	int val;
241 
242 	/*
243 	 * On an exchange command, we expect the following:
244 	 *
245   * <src ET> <src EU> <dst1 ET> <dst1 EU> [<dst2 ET> <dst2 EU>] [inv1] [inv2]
246 	 *
247 	 * where ET == element type and EU == element unit.
248 	 */
249 
250 	++argv; --argc;
251 
252 	if (argc < 4) {
253 		warnx("%s: too few arguments", cname);
254 		goto usage;
255 	} else if (argc > 8) {
256 		warnx("%s: too many arguments", cname);
257 		goto usage;
258 	}
259 	bzero(&cmd, sizeof(cmd));
260 
261 	/* <src ET>  */
262 	cmd.ce_srctype = parse_element_type(*argv);
263 	++argv; --argc;
264 
265 	/* <src EU> */
266 	cmd.ce_srcunit = parse_element_unit(*argv);
267 	++argv; --argc;
268 
269 	/* <dst1 ET> */
270 	cmd.ce_fdsttype = parse_element_type(*argv);
271 	++argv; --argc;
272 
273 	/* <dst1 EU> */
274 	cmd.ce_fdstunit = parse_element_unit(*argv);
275 	++argv; --argc;
276 
277 	/*
278 	 * If the next token is a special word or there are no more
279 	 * arguments, then this is a case of simple exchange.
280 	 * dst2 == src.
281 	 */
282 	if ((argc == 0) || is_special(*argv)) {
283 		cmd.ce_sdsttype = cmd.ce_srctype;
284 		cmd.ce_sdstunit = cmd.ce_srcunit;
285 		goto do_special;
286 	}
287 
288 	/* <dst2 ET> */
289 	cmd.ce_sdsttype = parse_element_type(*argv);
290 	++argv; --argc;
291 
292 	/* <dst2 EU> */
293 	cmd.ce_sdstunit = parse_element_unit(*argv);
294 	++argv; --argc;
295 
296  do_special:
297 	/* Deal with optional command modifiers. */
298 	while (argc) {
299 		val = parse_special(*argv);
300 		++argv; --argc;
301 		switch (val) {
302 		case SW_INVERT1:
303 			cmd.ce_flags |= CE_INVERT1;
304 			break;
305 
306 		case SW_INVERT2:
307 			cmd.ce_flags |= CE_INVERT2;
308 			break;
309 
310 		default:
311 			errx(1, "%s: inappropriate modifier `%s'",
312 			    cname, *argv);
313 			/* NOTREACHED */
314 		}
315 	}
316 
317 	/* Send command to changer. */
318 	if (ioctl(changer_fd, CHIOEXCHANGE, &cmd) == -1)
319 		err(1, "%s: CHIOEXCHANGE", changer_name);
320 
321 	return (0);
322 
323  usage:
324 	fprintf(stderr, "usage: %s %s <src ET> <src EU> <dst1 ET> <dst1 EU>\n"
325 	    "       [<dst2 ET> <dst2 EU>] [inv1] [inv2]\n",
326 	    __progname, cname);
327 	return (1);
328 }
329 
330 static int
do_position(char * cname,int argc,char * argv[])331 do_position(char *cname, int argc, char *argv[])
332 {
333 	struct changer_position cmd;
334 	int val;
335 
336 	/*
337 	 * On a position command, we expect the following:
338 	 *
339 	 * <to ET> <to EU> [inv]
340 	 *
341 	 * where ET == element type and EU == element unit.
342 	 */
343 
344 	++argv; --argc;
345 
346 	if (argc < 2) {
347 		warnx("%s: too few arguments", cname);
348 		goto usage;
349 	} else if (argc > 3) {
350 		warnx("%s: too many arguments", cname);
351 		goto usage;
352 	}
353 	bzero(&cmd, sizeof(cmd));
354 
355 	/* <to ET>  */
356 	cmd.cp_type = parse_element_type(*argv);
357 	++argv; --argc;
358 
359 	/* <to EU> */
360 	cmd.cp_unit = parse_element_unit(*argv);
361 	++argv; --argc;
362 
363 	/* Deal with optional command modifier. */
364 	if (argc) {
365 		val = parse_special(*argv);
366 		switch (val) {
367 		case SW_INVERT:
368 			cmd.cp_flags |= CP_INVERT;
369 			break;
370 
371 		default:
372 			errx(1, "%s: inappropriate modifier `%s'",
373 			    cname, *argv);
374 			/* NOTREACHED */
375 		}
376 	}
377 
378 	/* Send command to changer. */
379 	if (ioctl(changer_fd, CHIOPOSITION, &cmd) == -1)
380 		err(1, "%s: CHIOPOSITION", changer_name);
381 
382 	return (0);
383 
384  usage:
385 	fprintf(stderr, "usage: %s %s <to ET> <to EU> [inv]\n",
386 	    __progname, cname);
387 	return (1);
388 }
389 
390 static int
do_params(char * cname,int argc,char * argv[])391 do_params(char *cname, int argc, char *argv[])
392 {
393 	struct changer_params data;
394 
395 	/* No arguments to this command. */
396 
397 	++argv; --argc;
398 
399 	if (argc) {
400 		warnx("%s: no arguments expected", cname);
401 		goto usage;
402 	}
403 
404 	/* Get params from changer and display them. */
405 	bzero(&data, sizeof(data));
406 	if (ioctl(changer_fd, CHIOGPARAMS, &data) == -1)
407 		err(1, "%s: CHIOGPARAMS", changer_name);
408 
409 	printf("%s: %d slot%s, %d drive%s, %d picker%s",
410 	    changer_name,
411 	    data.cp_nslots, (data.cp_nslots > 1) ? "s" : "",
412 	    data.cp_ndrives, (data.cp_ndrives > 1) ? "s" : "",
413 	    data.cp_npickers, (data.cp_npickers > 1) ? "s" : "");
414 	if (data.cp_nportals)
415 		printf(", %d portal%s", data.cp_nportals,
416 		    (data.cp_nportals > 1) ? "s" : "");
417 	printf("\n%s: current picker: %d\n", changer_name, data.cp_curpicker);
418 
419 	return (0);
420 
421  usage:
422 	fprintf(stderr, "usage: %s %s\n", __progname, cname);
423 	return (1);
424 }
425 
426 static int
do_getpicker(char * cname,int argc,char * argv[])427 do_getpicker(char *cname, int argc, char *argv[])
428 {
429 	int picker;
430 
431 	/* No arguments to this command. */
432 
433 	++argv; --argc;
434 
435 	if (argc) {
436 		warnx("%s: no arguments expected", cname);
437 		goto usage;
438 	}
439 
440 	/* Get current picker from changer and display it. */
441 	if (ioctl(changer_fd, CHIOGPICKER, &picker) == -1)
442 		err(1, "%s: CHIOGPICKER", changer_name);
443 
444 	printf("%s: current picker: %d\n", changer_name, picker);
445 
446 	return (0);
447 
448  usage:
449 	fprintf(stderr, "usage: %s %s\n", __progname, cname);
450 	return (1);
451 }
452 
453 static int
do_setpicker(char * cname,int argc,char * argv[])454 do_setpicker(char *cname, int argc, char *argv[])
455 {
456 	int picker;
457 
458 	++argv; --argc;
459 
460 	if (argc < 1) {
461 		warnx("%s: too few arguments", cname);
462 		goto usage;
463 	} else if (argc > 1) {
464 		warnx("%s: too many arguments", cname);
465 		goto usage;
466 	}
467 
468 	picker = parse_element_unit(*argv);
469 
470 	/* Set the changer picker. */
471 	if (ioctl(changer_fd, CHIOSPICKER, &picker) == -1)
472 		err(1, "%s: CHIOSPICKER", changer_name);
473 
474 	return (0);
475 
476  usage:
477 	fprintf(stderr, "usage: %s %s <picker>\n", __progname, cname);
478 	return (1);
479 }
480 
481 static int
do_status(char * cname,int argc,char * argv[])482 do_status(char *cname, int argc, char *argv[])
483 {
484 	struct changer_element_status_request cmd;
485 	struct changer_params data;
486 	int i, chet, schet, echet, c;
487 	char *description;
488 	size_t count;
489 
490 	optreset = 1;
491 	optind = 1;
492 	while ((c = getopt(argc, argv, "SsvVa")) != -1) {
493 		switch (c) {
494 		case 's':
495 			sense = 1;
496 			break;
497 		case 'S':
498 			source = 1;
499 			break;
500 		case 'v':
501 			pvoltag = 1;
502 			break;
503 		case 'V':
504 			avoltag = 1;
505 			break;
506 		case 'a':
507 			pvoltag = avoltag = source = sense = 1;
508 			break;
509 		default:
510 			goto usage;
511 		}
512 	}
513 
514 	argc -= optind;
515 	argv += optind;
516 
517 	/*
518 	 * On a status command, we expect the following:
519 	 *
520 	 * [<ET>]
521 	 *
522 	 * where ET == element type.
523 	 *
524 	 * If we get no arguments, we get the status of all
525 	 * known element types.
526 	 */
527 	if (argc > 1) {
528 		warnx("%s: too many arguments", cname);
529 		goto usage;
530 	}
531 
532 	/*
533 	 * Get params from changer.  Specifically, we need the element
534 	 * counts.
535 	 */
536 	bzero(&data, sizeof(data));
537 	if (ioctl(changer_fd, CHIOGPARAMS, &data) == -1)
538 		err(1, "%s: CHIOGPARAMS", changer_name);
539 
540 	if (argc)
541 		schet = echet = parse_element_type(*argv);
542 	else {
543 		schet = CHET_MT;
544 		echet = CHET_DT;
545 	}
546 
547 	for (chet = schet; chet <= echet; ++chet) {
548 		switch (chet) {
549 		case CHET_MT:
550 			count = data.cp_npickers;
551 			description = "picker";
552 			break;
553 
554 		case CHET_ST:
555 			count = data.cp_nslots;
556 			description = "slot";
557 			break;
558 
559 		case CHET_IE:
560 			count = data.cp_nportals;
561 			description = "portal";
562 			break;
563 
564 		case CHET_DT:
565 			count = data.cp_ndrives;
566 			description = "drive";
567 			break;
568 		}
569 
570 		if (count == 0) {
571 			if (argc == 0)
572 				continue;
573 			else {
574 				printf("%s: no %s elements\n",
575 				    changer_name, description);
576 				return (0);
577 			}
578 		}
579 
580 		bzero(&cmd, sizeof(cmd));
581 
582 		cmd.cesr_type = chet;
583 		/* Allocate storage for the status info. */
584 		cmd.cesr_data = calloc(count, sizeof(*cmd.cesr_data));
585 		if ((cmd.cesr_data) == NULL)
586 			errx(1, "can't allocate status storage");
587 		if (avoltag || pvoltag)
588 			cmd.cesr_flags |= CESR_VOLTAGS;
589 
590 		if (ioctl(changer_fd, CHIOGSTATUS, &cmd) == -1) {
591 			free(cmd.cesr_data);
592 			err(1, "%s: CHIOGSTATUS", changer_name);
593 		}
594 
595 		/* Dump the status for each element of this type. */
596 		for (i = 0; i < count; ++i) {
597 			struct changer_element_status *ces =
598 			         &(cmd.cesr_data[i]);
599 			printf("%s %d: %s", description, i,
600 			    bits_to_string(ces->ces_flags, CESTATUS_BITS));
601 			if (sense)
602 				printf(" sense: <0x%02x/0x%02x>",
603 				       ces->ces_sensecode,
604 				       ces->ces_sensequal);
605 			if (pvoltag)
606 				printf(" voltag: <%s:%d>",
607 				       ces->ces_pvoltag.cv_volid,
608 				       ces->ces_pvoltag.cv_serial);
609 			if (avoltag)
610 				printf(" avoltag: <%s:%d>",
611 				       ces->ces_avoltag.cv_volid,
612 				       ces->ces_avoltag.cv_serial);
613 			if (source) {
614 				if (ces->ces_flags & CESTATUS_ACCESS)
615 					printf(" source: <%s %d>",
616 						element_type_name(
617 							ces->ces_source_type),
618 						ces->ces_source_addr);
619 				else
620 					printf(" source: <>");
621 			}
622 			printf("\n");
623 		}
624 
625 		free(cmd.cesr_data);
626 	}
627 
628 	return (0);
629 
630  usage:
631 	fprintf(stderr, "usage: %s %s [<element type>]\n", __progname,
632 	    cname);
633 	return (1);
634 }
635 
636 /*
637  * Check a drive unit as the source for a move or exchange
638  * operation. If the drive is not accessible, we attempt
639  * to unmount the tape in it before moving to avoid
640  * errors in "disconnected" type pickers where the drive
641  * is on a separate target from the changer.
642  */
643 static void
check_source_drive(int unit)644 check_source_drive(int unit)
645 {
646 	struct mtop mtoffl =  { MTOFFL, 1 };
647 	struct changer_element_status_request cmd;
648 	struct changer_element_status *ces;
649 	struct changer_params data;
650 	size_t count = 0;
651 	int mtfd;
652 	char *tapedev;
653 
654 	/*
655 	 * Get params from changer.  Specifically, we need the element
656 	 * counts.
657 	 */
658 	bzero(&data, sizeof(data));
659 	if (ioctl(changer_fd, CHIOGPARAMS, &data) == -1)
660 		err(1, "%s: CHIOGPARAMS", changer_name);
661 
662 	count = data.cp_ndrives;
663 	if (unit < 0 || unit >= count)
664 		err(1, "%s: invalid drive: drive %d", changer_name, unit);
665 
666 	bzero(&cmd, sizeof(cmd));
667 	cmd.cesr_type = CHET_DT;
668 	/* Allocate storage for the status info. */
669 	cmd.cesr_data = calloc(count, sizeof(*cmd.cesr_data));
670 	if ((cmd.cesr_data) == NULL)
671 		errx(1, "can't allocate status storage");
672 
673 	if (ioctl(changer_fd, CHIOGSTATUS, &cmd) == -1) {
674 		free(cmd.cesr_data);
675 		err(1, "%s: CHIOGSTATUS", changer_name);
676 	}
677 	ces = &(cmd.cesr_data[unit]);
678 
679 	if ((ces->ces_flags & CESTATUS_FULL) != CESTATUS_FULL)
680 		err(1, "%s: drive %d is empty!", changer_name, unit);
681 
682 	if ((ces->ces_flags & CESTATUS_ACCESS) == CESTATUS_ACCESS)
683 		return; /* changer thinks all is well - trust it */
684 
685 	/*
686 	 * Otherwise, drive is FULL, but not accessible.
687 	 * Try to make it accessible by doing an mt offline.
688 	 */
689 	tapedev = parse_tapedev(_PATH_CH_CONF, changer_name, unit);
690 	mtfd = opendev(tapedev, O_RDONLY, 0, NULL);
691 	if (mtfd == -1)
692 		err(1, "%s drive %d (%s): open", changer_name, unit, tapedev);
693 	if (ioctl(mtfd, MTIOCTOP, &mtoffl) == -1)
694 		err(1, "%s drive %d (%s): rewoffl", changer_name, unit,
695 		    tapedev);
696 	close(mtfd);
697 }
698 
699 void
find_voltag(char * voltag,int * type,int * unit)700 find_voltag(char *voltag, int *type, int *unit)
701 {
702 	struct changer_element_status_request cmd;
703 	struct changer_params data;
704 	int i, chet, schet, echet, found;
705 	size_t count = 0;
706 
707 	/*
708 	 * Get params from changer.  Specifically, we need the element
709 	 * counts.
710 	 */
711 	bzero(&data, sizeof(data));
712 	if (ioctl(changer_fd, CHIOGPARAMS, &data) == -1)
713 		err(1, "%s: CHIOGPARAMS", changer_name);
714 
715 	found = 0;
716 	schet = CHET_MT;
717 	echet = CHET_DT;
718 
719 	/*
720 	 * For each type of element, iterate through each one until
721 	 * we find the correct volume id.
722 	 */
723 	for (chet = schet; chet <= echet; ++chet) {
724 		switch (chet) {
725 		case CHET_MT:
726 			count = data.cp_npickers;
727 			break;
728 		case CHET_ST:
729 			count = data.cp_nslots;
730 			break;
731 		case CHET_IE:
732 			count = data.cp_nportals;
733 			break;
734 		case CHET_DT:
735 			count = data.cp_ndrives;
736 			break;
737 		}
738 		if (count == 0 || found)
739 			continue;
740 
741 		bzero(&cmd, sizeof(cmd));
742 		cmd.cesr_type = chet;
743 		/* Allocate storage for the status info. */
744 		cmd.cesr_data = calloc(count, sizeof(*cmd.cesr_data));
745 		if ((cmd.cesr_data) == NULL)
746 			errx(1, "can't allocate status storage");
747 		cmd.cesr_flags |= CESR_VOLTAGS;
748 
749 		if (ioctl(changer_fd, CHIOGSTATUS, &cmd) == -1) {
750 			free(cmd.cesr_data);
751 			err(1, "%s: CHIOGSTATUS", changer_name);
752 		}
753 
754 		/*
755 		 * look through each element to see if it has our desired
756 		 * volume tag.
757 		 */
758 		for (i = 0; i < count; ++i) {
759 			struct changer_element_status *ces =
760 			    &(cmd.cesr_data[i]);
761 			if ((ces->ces_flags & CESTATUS_FULL) != CESTATUS_FULL)
762 				continue; /* no tape in drive */
763 			if (strcasecmp(voltag, ces->ces_pvoltag.cv_volid)
764 			    == 0) {
765 				*type = chet;
766 				*unit = i;
767 				found = 1;
768 				free(cmd.cesr_data);
769 				return;
770 			}
771 		}
772 		free(cmd.cesr_data);
773 	}
774 	errx(1, "%s: unable to locate voltag: %s", changer_name, voltag);
775 }
776 
777 
778 static int
parse_element_type(char * cp)779 parse_element_type(char *cp)
780 {
781 	int i;
782 
783 	for (i = 0; elements[i].et_name != NULL; ++i)
784 		if (strcmp(elements[i].et_name, cp) == 0)
785 			return (elements[i].et_type);
786 
787 	errx(1, "invalid element type `%s'", cp);
788 }
789 
790 static const char *
element_type_name(int et)791 element_type_name(int et)
792 {
793 	int i;
794 
795 	for (i = 0; elements[i].et_name != NULL; i++)
796 		if (elements[i].et_type == et)
797 			return elements[i].et_name;
798 
799 	return "unknown";
800 }
801 
802 static int
parse_element_unit(char * cp)803 parse_element_unit(char *cp)
804 {
805 	int i;
806 	char *p;
807 
808 	i = (int)strtol(cp, &p, 10);
809 	if ((i < 0) || (*p != '\0'))
810 		errx(1, "invalid unit number `%s'", cp);
811 
812 	return (i);
813 }
814 
815 static int
parse_special(char * cp)816 parse_special(char *cp)
817 {
818 	int val;
819 
820 	val = is_special(cp);
821 	if (val)
822 		return (val);
823 
824 	errx(1, "invalid modifier `%s'", cp);
825 }
826 
827 static int
is_special(char * cp)828 is_special(char *cp)
829 {
830 	int i;
831 
832 	for (i = 0; specials[i].sw_name != NULL; ++i)
833 		if (strcmp(specials[i].sw_name, cp) == 0)
834 			return (specials[i].sw_value);
835 
836 	return (0);
837 }
838 
839 static char *
bits_to_string(int v,const char * cp)840 bits_to_string(int v, const char *cp)
841 {
842 	const char *np;
843 	char f, sep, *bp;
844 	static char buf[128];
845 
846 	bp = buf;
847 	bzero(buf, sizeof(buf));
848 
849 	for (sep = '<'; (f = *cp++) != 0; cp = np) {
850 		for (np = cp; *np >= ' ';)
851 			np++;
852 		if ((v & (1 << (f - 1))) == 0)
853 			continue;
854 		(void)snprintf(bp, sizeof(buf) - (bp - &buf[0]),
855 		    "%c%.*s", sep, (int)(np - cp), cp);
856 		bp += strlen(bp);
857 		sep = ',';
858 	}
859 	if (sep != '<')
860 		*bp = '>';
861 
862 	return (buf);
863 }
864 
865 static void
usage(void)866 usage(void)
867 {
868 	fprintf(stderr, "usage: %s [-f changer] command [arg ...]\n",
869 	    __progname);
870 	exit(1);
871 }
872