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