GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: bin/chio/chio.c Lines: 0 325 0.0 %
Date: 2017-11-07 Branches: 0 178 0.0 %

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