xref: /openbsd/bin/chio/chio.c (revision c4a732d1)
1*c4a732d1Sbeck /*	$OpenBSD: chio.c,v 1.16 2006/05/31 03:04:52 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 *);
63c16b380fSderaadt 
64c72b5b24Smillert static	int do_move(char *, int, char **);
65c72b5b24Smillert static	int do_exchange(char *, int, char **);
66c72b5b24Smillert static	int do_position(char *, int, char **);
67c72b5b24Smillert static	int do_params(char *, int, char **);
68c72b5b24Smillert static	int do_getpicker(char *, int, char **);
69c72b5b24Smillert static	int do_setpicker(char *, int, char **);
70c72b5b24Smillert static	int do_status(char *, int, char **);
71c16b380fSderaadt 
72c16b380fSderaadt /* Valid changer element types. */
73c16b380fSderaadt const struct element_type elements[] = {
74c16b380fSderaadt 	{ "drive",		CHET_DT },
75b13cd761Sderaadt 	{ "picker",		CHET_MT },
76b13cd761Sderaadt 	{ "portal",		CHET_IE },
77b13cd761Sderaadt 	{ "slot",		CHET_ST },
78c16b380fSderaadt 	{ NULL,			0 },
79c16b380fSderaadt };
80c16b380fSderaadt 
81c16b380fSderaadt /* Valid commands. */
82c16b380fSderaadt const struct changer_command commands[] = {
83c16b380fSderaadt 	{ "exchange",		do_exchange },
84c16b380fSderaadt 	{ "getpicker",		do_getpicker },
85b13cd761Sderaadt 	{ "move",		do_move },
86b13cd761Sderaadt 	{ "params",		do_params },
87b13cd761Sderaadt 	{ "position",		do_position },
88c16b380fSderaadt 	{ "setpicker",		do_setpicker },
89c16b380fSderaadt 	{ "status",		do_status },
90c16b380fSderaadt 	{ NULL,			0 },
91c16b380fSderaadt };
92c16b380fSderaadt 
93c16b380fSderaadt /* Valid special words. */
94c16b380fSderaadt const struct special_word specials[] = {
95c16b380fSderaadt 	{ "inv",		SW_INVERT },
96c16b380fSderaadt 	{ "inv1",		SW_INVERT1 },
97c16b380fSderaadt 	{ "inv2",		SW_INVERT2 },
98c16b380fSderaadt 	{ NULL,			0 },
99c16b380fSderaadt };
100c16b380fSderaadt 
101c16b380fSderaadt static	int changer_fd;
102c16b380fSderaadt static	char *changer_name;
103*c4a732d1Sbeck static int avoltag;
104*c4a732d1Sbeck static int pvoltag;
105c16b380fSderaadt 
106c16b380fSderaadt int
107ab83b6d6Sderaadt main(int argc, char *argv[])
108c16b380fSderaadt {
109c16b380fSderaadt 	int ch, i;
110c16b380fSderaadt 
111*c4a732d1Sbeck 	while ((ch = getopt(argc, argv, "af:vV")) != -1) {
112c16b380fSderaadt 		switch (ch) {
113*c4a732d1Sbeck 		case 'v':
114*c4a732d1Sbeck 			pvoltag = 1;
115*c4a732d1Sbeck 			break;
116*c4a732d1Sbeck 		case 'V':
117*c4a732d1Sbeck 			avoltag = 1;
118*c4a732d1Sbeck 			break;
119c16b380fSderaadt 		case 'f':
120c16b380fSderaadt 			changer_name = optarg;
121c16b380fSderaadt 			break;
122*c4a732d1Sbeck 		case 'a':
123*c4a732d1Sbeck 			pvoltag = avoltag = 1;
124*c4a732d1Sbeck 			break;
125c16b380fSderaadt 		default:
126c16b380fSderaadt 			usage();
127c16b380fSderaadt 		}
128c16b380fSderaadt 	}
129c16b380fSderaadt 	argc -= optind;
130c16b380fSderaadt 	argv += optind;
131c16b380fSderaadt 
132c16b380fSderaadt 	if (argc == 0)
133c16b380fSderaadt 		usage();
134c16b380fSderaadt 
135c16b380fSderaadt 	/* Get the default changer if not already specified. */
136c16b380fSderaadt 	if (changer_name == NULL)
137c16b380fSderaadt 		if ((changer_name = getenv(CHANGER_ENV_VAR)) == NULL)
138c16b380fSderaadt 			changer_name = _PATH_CH;
139c16b380fSderaadt 
140c16b380fSderaadt 	/* Open the changer device. */
141c16b380fSderaadt 	if ((changer_fd = open(changer_name, O_RDWR, 0600)) == -1)
142c16b380fSderaadt 		err(1, "%s: open", changer_name);
143c16b380fSderaadt 
144c16b380fSderaadt 	/* Find the specified command. */
145c16b380fSderaadt 	for (i = 0; commands[i].cc_name != NULL; ++i)
146c16b380fSderaadt 		if (strcmp(*argv, commands[i].cc_name) == 0)
147c16b380fSderaadt 			break;
148b13cd761Sderaadt 	if (commands[i].cc_name == NULL) {
149b13cd761Sderaadt 		/* look for abbreviation */
150b13cd761Sderaadt 		for (i = 0; commands[i].cc_name != NULL; ++i)
151b13cd761Sderaadt 			if (strncmp(*argv, commands[i].cc_name,
152b13cd761Sderaadt 			    strlen(*argv)) == 0)
153b13cd761Sderaadt 				break;
154b13cd761Sderaadt 	}
155c16b380fSderaadt 	if (commands[i].cc_name == NULL)
156c16b380fSderaadt 		errx(1, "unknown command: %s", *argv);
157c16b380fSderaadt 
158c16b380fSderaadt 	/* Skip over the command name and call handler. */
159c16b380fSderaadt 	++argv; --argc;
160c16b380fSderaadt 	exit((*commands[i].cc_handler)(commands[i].cc_name, argc, argv));
161c16b380fSderaadt }
162c16b380fSderaadt 
163c16b380fSderaadt static int
164ab83b6d6Sderaadt do_move(char *cname, int argc, char *argv[])
165c16b380fSderaadt {
166c16b380fSderaadt 	struct changer_move cmd;
167c16b380fSderaadt 	int val;
168c16b380fSderaadt 
169c16b380fSderaadt 	/*
170c16b380fSderaadt 	 * On a move command, we expect the following:
171c16b380fSderaadt 	 *
172c16b380fSderaadt 	 * <from ET> <from EU> <to ET> <to EU> [inv]
173c16b380fSderaadt 	 *
174c16b380fSderaadt 	 * where ET == element type and EU == element unit.
175c16b380fSderaadt 	 */
176c16b380fSderaadt 	if (argc < 4) {
177c16b380fSderaadt 		warnx("%s: too few arguments", cname);
178c16b380fSderaadt 		goto usage;
179c16b380fSderaadt 	} else if (argc > 5) {
180c16b380fSderaadt 		warnx("%s: too many arguments", cname);
181c16b380fSderaadt 		goto usage;
182c16b380fSderaadt 	}
183c16b380fSderaadt 	bzero(&cmd, sizeof(cmd));
184c16b380fSderaadt 
185c16b380fSderaadt 	/* <from ET>  */
186c16b380fSderaadt 	cmd.cm_fromtype = parse_element_type(*argv);
187c16b380fSderaadt 	++argv; --argc;
188c16b380fSderaadt 
189c16b380fSderaadt 	/* <from EU> */
190c16b380fSderaadt 	cmd.cm_fromunit = parse_element_unit(*argv);
191c16b380fSderaadt 	++argv; --argc;
192c16b380fSderaadt 
193b2b21931Sbeck 	if (cmd.cm_fromtype == CHET_DT) {
194b2b21931Sbeck 		/*
195b2b21931Sbeck 		 * from unit is a drive - make sure the tape
196b2b21931Sbeck 		 * in it is unmounted before we attempt to move
197b2b21931Sbeck 		 * it to avoid errors in "disconnected" type
198b2b21931Sbeck 		 * pickers where the drive is on a seperate target
199b2b21931Sbeck 		 * from the changer.
200b2b21931Sbeck 		 */
201b2b21931Sbeck 		int mtfd;
202b2b21931Sbeck 		struct mtop mtoffl =  { MTOFFL, 1 };
203252f0782Shenning 		char *tapedev =
204252f0782Shenning 		    parse_tapedev(_PATH_CH_CONF, changer_name, cmd.cm_fromunit);
205b2b21931Sbeck 		mtfd = opendev(tapedev, O_RDONLY, OPENDEV_PART | OPENDEV_DRCT,
206b2b21931Sbeck 		    NULL);
207b2b21931Sbeck 		if (mtfd == -1)
208b2b21931Sbeck 			err(1, "%s drive %d (%s): open", changer_name,
209b2b21931Sbeck 			    cmd.cm_fromunit, tapedev);
210b2b21931Sbeck 		if (ioctl(mtfd, MTIOCTOP, &mtoffl) == -1)
211b2b21931Sbeck 			err(1, "%s drive %d (%s): rewoffl", changer_name,
212b2b21931Sbeck 			    cmd.cm_fromunit, tapedev);
213b2b21931Sbeck 		close(mtfd);
214b2b21931Sbeck 	}
215b2b21931Sbeck 
216c16b380fSderaadt 	/* <to ET> */
217c16b380fSderaadt 	cmd.cm_totype = parse_element_type(*argv);
218c16b380fSderaadt 	++argv; --argc;
219c16b380fSderaadt 
220c16b380fSderaadt 	/* <to EU> */
221c16b380fSderaadt 	cmd.cm_tounit = parse_element_unit(*argv);
222c16b380fSderaadt 	++argv; --argc;
223c16b380fSderaadt 
224c16b380fSderaadt 	/* Deal with optional command modifier. */
225c16b380fSderaadt 	if (argc) {
226c16b380fSderaadt 		val = parse_special(*argv);
227c16b380fSderaadt 		switch (val) {
228c16b380fSderaadt 		case SW_INVERT:
229c16b380fSderaadt 			cmd.cm_flags |= CM_INVERT;
230c16b380fSderaadt 			break;
231c16b380fSderaadt 
232c16b380fSderaadt 		default:
233c16b380fSderaadt 			errx(1, "%s: inappropriate modifier `%s'",
234c16b380fSderaadt 			    cname, *argv);
235c16b380fSderaadt 			/* NOTREACHED */
236c16b380fSderaadt 		}
237c16b380fSderaadt 	}
238c16b380fSderaadt 
239c16b380fSderaadt 	/* Send command to changer. */
240496bd962Sderaadt 	if (ioctl(changer_fd, CHIOMOVE, &cmd))
241c16b380fSderaadt 		err(1, "%s: CHIOMOVE", changer_name);
242c16b380fSderaadt 
243c16b380fSderaadt 	return (0);
244c16b380fSderaadt 
245c16b380fSderaadt  usage:
246c16b380fSderaadt 	fprintf(stderr, "usage: %s %s "
247c16b380fSderaadt 	    "<from ET> <from EU> <to ET> <to EU> [inv]\n", __progname, cname);
248c16b380fSderaadt 	return (1);
249c16b380fSderaadt }
250c16b380fSderaadt 
251c16b380fSderaadt static int
252ab83b6d6Sderaadt do_exchange(char *cname, int argc, char *argv[])
253c16b380fSderaadt {
254c16b380fSderaadt 	struct changer_exchange cmd;
255c16b380fSderaadt 	int val;
256c16b380fSderaadt 
257c16b380fSderaadt 	/*
258c16b380fSderaadt 	 * On an exchange command, we expect the following:
259c16b380fSderaadt 	 *
260c16b380fSderaadt   * <src ET> <src EU> <dst1 ET> <dst1 EU> [<dst2 ET> <dst2 EU>] [inv1] [inv2]
261c16b380fSderaadt 	 *
262c16b380fSderaadt 	 * where ET == element type and EU == element unit.
263c16b380fSderaadt 	 */
264c16b380fSderaadt 	if (argc < 4) {
265c16b380fSderaadt 		warnx("%s: too few arguments", cname);
266c16b380fSderaadt 		goto usage;
267c16b380fSderaadt 	} else if (argc > 8) {
268c16b380fSderaadt 		warnx("%s: too many arguments", cname);
269c16b380fSderaadt 		goto usage;
270c16b380fSderaadt 	}
271c16b380fSderaadt 	bzero(&cmd, sizeof(cmd));
272c16b380fSderaadt 
273c16b380fSderaadt 	/* <src ET>  */
274c16b380fSderaadt 	cmd.ce_srctype = parse_element_type(*argv);
275c16b380fSderaadt 	++argv; --argc;
276c16b380fSderaadt 
277c16b380fSderaadt 	/* <src EU> */
278c16b380fSderaadt 	cmd.ce_srcunit = parse_element_unit(*argv);
279c16b380fSderaadt 	++argv; --argc;
280c16b380fSderaadt 
281c16b380fSderaadt 	/* <dst1 ET> */
282c16b380fSderaadt 	cmd.ce_fdsttype = parse_element_type(*argv);
283c16b380fSderaadt 	++argv; --argc;
284c16b380fSderaadt 
285c16b380fSderaadt 	/* <dst1 EU> */
286c16b380fSderaadt 	cmd.ce_fdstunit = parse_element_unit(*argv);
287c16b380fSderaadt 	++argv; --argc;
288c16b380fSderaadt 
289c16b380fSderaadt 	/*
290c16b380fSderaadt 	 * If the next token is a special word or there are no more
291c16b380fSderaadt 	 * arguments, then this is a case of simple exchange.
292c16b380fSderaadt 	 * dst2 == src.
293c16b380fSderaadt 	 */
294c16b380fSderaadt 	if ((argc == 0) || is_special(*argv)) {
295c16b380fSderaadt 		cmd.ce_sdsttype = cmd.ce_srctype;
296c16b380fSderaadt 		cmd.ce_sdstunit = cmd.ce_srcunit;
297c16b380fSderaadt 		goto do_special;
298c16b380fSderaadt 	}
299c16b380fSderaadt 
300c16b380fSderaadt 	/* <dst2 ET> */
301c16b380fSderaadt 	cmd.ce_sdsttype = parse_element_type(*argv);
302c16b380fSderaadt 	++argv; --argc;
303c16b380fSderaadt 
304c16b380fSderaadt 	/* <dst2 EU> */
305c16b380fSderaadt 	cmd.ce_sdstunit = parse_element_unit(*argv);
306c16b380fSderaadt 	++argv; --argc;
307c16b380fSderaadt 
308c16b380fSderaadt  do_special:
309c16b380fSderaadt 	/* Deal with optional command modifiers. */
310c16b380fSderaadt 	while (argc) {
311c16b380fSderaadt 		val = parse_special(*argv);
312c16b380fSderaadt 		++argv; --argc;
313c16b380fSderaadt 		switch (val) {
314c16b380fSderaadt 		case SW_INVERT1:
315c16b380fSderaadt 			cmd.ce_flags |= CE_INVERT1;
316c16b380fSderaadt 			break;
317c16b380fSderaadt 
318c16b380fSderaadt 		case SW_INVERT2:
319c16b380fSderaadt 			cmd.ce_flags |= CE_INVERT2;
320c16b380fSderaadt 			break;
321c16b380fSderaadt 
322c16b380fSderaadt 		default:
323c16b380fSderaadt 			errx(1, "%s: inappropriate modifier `%s'",
324c16b380fSderaadt 			    cname, *argv);
325c16b380fSderaadt 			/* NOTREACHED */
326c16b380fSderaadt 		}
327c16b380fSderaadt 	}
328c16b380fSderaadt 
329c16b380fSderaadt 	/* Send command to changer. */
330496bd962Sderaadt 	if (ioctl(changer_fd, CHIOEXCHANGE, &cmd))
331c16b380fSderaadt 		err(1, "%s: CHIOEXCHANGE", changer_name);
332c16b380fSderaadt 
333c16b380fSderaadt 	return (0);
334c16b380fSderaadt 
335c16b380fSderaadt  usage:
336c16b380fSderaadt 	fprintf(stderr, "usage: %s %s <src ET> <src EU> <dst1 ET> <dst1 EU>\n"
337c16b380fSderaadt 	    "       [<dst2 ET> <dst2 EU>] [inv1] [inv2]\n",
338c16b380fSderaadt 	    __progname, cname);
339c16b380fSderaadt 	return (1);
340c16b380fSderaadt }
341c16b380fSderaadt 
342c16b380fSderaadt static int
343ab83b6d6Sderaadt do_position(char *cname, int argc, char *argv[])
344c16b380fSderaadt {
345c16b380fSderaadt 	struct changer_position cmd;
346c16b380fSderaadt 	int val;
347c16b380fSderaadt 
348c16b380fSderaadt 	/*
349c16b380fSderaadt 	 * On a position command, we expect the following:
350c16b380fSderaadt 	 *
351c16b380fSderaadt 	 * <to ET> <to EU> [inv]
352c16b380fSderaadt 	 *
353c16b380fSderaadt 	 * where ET == element type and EU == element unit.
354c16b380fSderaadt 	 */
355c16b380fSderaadt 	if (argc < 2) {
356c16b380fSderaadt 		warnx("%s: too few arguments", cname);
357c16b380fSderaadt 		goto usage;
358c16b380fSderaadt 	} else if (argc > 3) {
359c16b380fSderaadt 		warnx("%s: too many arguments", cname);
360c16b380fSderaadt 		goto usage;
361c16b380fSderaadt 	}
362c16b380fSderaadt 	bzero(&cmd, sizeof(cmd));
363c16b380fSderaadt 
364c16b380fSderaadt 	/* <to ET>  */
365c16b380fSderaadt 	cmd.cp_type = parse_element_type(*argv);
366c16b380fSderaadt 	++argv; --argc;
367c16b380fSderaadt 
368c16b380fSderaadt 	/* <to EU> */
369c16b380fSderaadt 	cmd.cp_unit = parse_element_unit(*argv);
370c16b380fSderaadt 	++argv; --argc;
371c16b380fSderaadt 
372c16b380fSderaadt 	/* Deal with optional command modifier. */
373c16b380fSderaadt 	if (argc) {
374c16b380fSderaadt 		val = parse_special(*argv);
375c16b380fSderaadt 		switch (val) {
376c16b380fSderaadt 		case SW_INVERT:
377c16b380fSderaadt 			cmd.cp_flags |= CP_INVERT;
378c16b380fSderaadt 			break;
379c16b380fSderaadt 
380c16b380fSderaadt 		default:
381c16b380fSderaadt 			errx(1, "%s: inappropriate modifier `%s'",
382c16b380fSderaadt 			    cname, *argv);
383c16b380fSderaadt 			/* NOTREACHED */
384c16b380fSderaadt 		}
385c16b380fSderaadt 	}
386c16b380fSderaadt 
387c16b380fSderaadt 	/* Send command to changer. */
388496bd962Sderaadt 	if (ioctl(changer_fd, CHIOPOSITION, &cmd))
389c16b380fSderaadt 		err(1, "%s: CHIOPOSITION", changer_name);
390c16b380fSderaadt 
391c16b380fSderaadt 	return (0);
392c16b380fSderaadt 
393c16b380fSderaadt  usage:
394c16b380fSderaadt 	fprintf(stderr, "usage: %s %s <to ET> <to EU> [inv]\n",
395c16b380fSderaadt 	    __progname, cname);
396c16b380fSderaadt 	return (1);
397c16b380fSderaadt }
398c16b380fSderaadt 
399c16b380fSderaadt static int
400ab83b6d6Sderaadt do_params(char *cname, int argc, char *argv[])
401c16b380fSderaadt {
402c16b380fSderaadt 	struct changer_params data;
403c16b380fSderaadt 
404c16b380fSderaadt 	/* No arguments to this command. */
405c16b380fSderaadt 	if (argc) {
406d9248734Stodd 		warnx("%s: no arguments expected", cname);
407c16b380fSderaadt 		goto usage;
408c16b380fSderaadt 	}
409c16b380fSderaadt 
410c16b380fSderaadt 	/* Get params from changer and display them. */
411c16b380fSderaadt 	bzero(&data, sizeof(data));
412496bd962Sderaadt 	if (ioctl(changer_fd, CHIOGPARAMS, &data))
413c16b380fSderaadt 		err(1, "%s: CHIOGPARAMS", changer_name);
414c16b380fSderaadt 
415c16b380fSderaadt 	printf("%s: %d slot%s, %d drive%s, %d picker%s",
416c16b380fSderaadt 	    changer_name,
417c16b380fSderaadt 	    data.cp_nslots, (data.cp_nslots > 1) ? "s" : "",
418c16b380fSderaadt 	    data.cp_ndrives, (data.cp_ndrives > 1) ? "s" : "",
419c16b380fSderaadt 	    data.cp_npickers, (data.cp_npickers > 1) ? "s" : "");
420c16b380fSderaadt 	if (data.cp_nportals)
421c16b380fSderaadt 		printf(", %d portal%s", data.cp_nportals,
422c16b380fSderaadt 		    (data.cp_nportals > 1) ? "s" : "");
423c16b380fSderaadt 	printf("\n%s: current picker: %d\n", changer_name, data.cp_curpicker);
424c16b380fSderaadt 
425c16b380fSderaadt 	return (0);
426c16b380fSderaadt 
427c16b380fSderaadt  usage:
428c16b380fSderaadt 	fprintf(stderr, "usage: %s %s\n", __progname, cname);
429c16b380fSderaadt 	return (1);
430c16b380fSderaadt }
431c16b380fSderaadt 
432c16b380fSderaadt static int
433ab83b6d6Sderaadt do_getpicker(char *cname, int argc, char *argv[])
434c16b380fSderaadt {
435c16b380fSderaadt 	int picker;
436c16b380fSderaadt 
437c16b380fSderaadt 	/* No arguments to this command. */
438c16b380fSderaadt 	if (argc) {
439c16b380fSderaadt 		warnx("%s: no arguments expected", cname);
440c16b380fSderaadt 		goto usage;
441c16b380fSderaadt 	}
442c16b380fSderaadt 
443c16b380fSderaadt 	/* Get current picker from changer and display it. */
444496bd962Sderaadt 	if (ioctl(changer_fd, CHIOGPICKER, &picker))
445c16b380fSderaadt 		err(1, "%s: CHIOGPICKER", changer_name);
446c16b380fSderaadt 
447c16b380fSderaadt 	printf("%s: current picker: %d\n", changer_name, picker);
448c16b380fSderaadt 
449c16b380fSderaadt 	return (0);
450c16b380fSderaadt 
451c16b380fSderaadt  usage:
452c16b380fSderaadt 	fprintf(stderr, "usage: %s %s\n", __progname, cname);
453c16b380fSderaadt 	return (1);
454c16b380fSderaadt }
455c16b380fSderaadt 
456c16b380fSderaadt static int
457ab83b6d6Sderaadt do_setpicker(char *cname, int argc, char *argv[])
458c16b380fSderaadt {
459c16b380fSderaadt 	int picker;
460c16b380fSderaadt 
461c16b380fSderaadt 	if (argc < 1) {
462c16b380fSderaadt 		warnx("%s: too few arguments", cname);
463c16b380fSderaadt 		goto usage;
464c16b380fSderaadt 	} else if (argc > 1) {
465c16b380fSderaadt 		warnx("%s: too many arguments", cname);
466c16b380fSderaadt 		goto usage;
467c16b380fSderaadt 	}
468c16b380fSderaadt 
469c16b380fSderaadt 	picker = parse_element_unit(*argv);
470c16b380fSderaadt 
471c16b380fSderaadt 	/* Set the changer picker. */
472496bd962Sderaadt 	if (ioctl(changer_fd, CHIOSPICKER, &picker))
473c16b380fSderaadt 		err(1, "%s: CHIOSPICKER", changer_name);
474c16b380fSderaadt 
475c16b380fSderaadt 	return (0);
476c16b380fSderaadt 
477c16b380fSderaadt  usage:
478c16b380fSderaadt 	fprintf(stderr, "usage: %s %s <picker>\n", __progname, cname);
479c16b380fSderaadt 	return (1);
480c16b380fSderaadt }
481c16b380fSderaadt 
482c16b380fSderaadt static int
483ab83b6d6Sderaadt do_status(char *cname, int argc, char *argv[])
484c16b380fSderaadt {
485*c4a732d1Sbeck 	struct changer_element_status_request cmd;
486c16b380fSderaadt 	struct changer_params data;
487496bd962Sderaadt 	int i, chet, schet, echet;
488764064c4Smickey 	char *description;
489496bd962Sderaadt 	size_t count;
490764064c4Smickey 
491764064c4Smickey #ifdef lint
492764064c4Smickey 	count = 0;
493764064c4Smickey 	description = NULL;
494764064c4Smickey #endif
495c16b380fSderaadt 
496c16b380fSderaadt 	/*
497c16b380fSderaadt 	 * On a status command, we expect the following:
498c16b380fSderaadt 	 *
499c16b380fSderaadt 	 * [<ET>]
500c16b380fSderaadt 	 *
501c16b380fSderaadt 	 * where ET == element type.
502c16b380fSderaadt 	 *
503c16b380fSderaadt 	 * If we get no arguments, we get the status of all
504c16b380fSderaadt 	 * known element types.
505c16b380fSderaadt 	 */
506c16b380fSderaadt 	if (argc > 1) {
507c16b380fSderaadt 		warnx("%s: too many arguments", cname);
508c16b380fSderaadt 		goto usage;
509c16b380fSderaadt 	}
510c16b380fSderaadt 
511c16b380fSderaadt 	/*
512c16b380fSderaadt 	 * Get params from changer.  Specifically, we need the element
513c16b380fSderaadt 	 * counts.
514c16b380fSderaadt 	 */
515c16b380fSderaadt 	bzero(&data, sizeof(data));
516496bd962Sderaadt 	if (ioctl(changer_fd, CHIOGPARAMS, &data))
517c16b380fSderaadt 		err(1, "%s: CHIOGPARAMS", changer_name);
518c16b380fSderaadt 
519c16b380fSderaadt 	if (argc)
520c16b380fSderaadt 		schet = echet = parse_element_type(*argv);
521c16b380fSderaadt 	else {
522c16b380fSderaadt 		schet = CHET_MT;
523c16b380fSderaadt 		echet = CHET_DT;
524c16b380fSderaadt 	}
525c16b380fSderaadt 
526c16b380fSderaadt 	for (chet = schet; chet <= echet; ++chet) {
527c16b380fSderaadt 		switch (chet) {
528c16b380fSderaadt 		case CHET_MT:
529c16b380fSderaadt 			count = data.cp_npickers;
530c16b380fSderaadt 			description = "picker";
531c16b380fSderaadt 			break;
532c16b380fSderaadt 
533c16b380fSderaadt 		case CHET_ST:
534c16b380fSderaadt 			count = data.cp_nslots;
535c16b380fSderaadt 			description = "slot";
536c16b380fSderaadt 			break;
537c16b380fSderaadt 
538c16b380fSderaadt 		case CHET_IE:
539c16b380fSderaadt 			count = data.cp_nportals;
540c16b380fSderaadt 			description = "portal";
541c16b380fSderaadt 			break;
542c16b380fSderaadt 
543c16b380fSderaadt 		case CHET_DT:
544c16b380fSderaadt 			count = data.cp_ndrives;
545c16b380fSderaadt 			description = "drive";
546c16b380fSderaadt 			break;
547c16b380fSderaadt 		}
548c16b380fSderaadt 
549c16b380fSderaadt 		if (count == 0) {
550c16b380fSderaadt 			if (argc == 0)
551c16b380fSderaadt 				continue;
552c16b380fSderaadt 			else {
553c16b380fSderaadt 				printf("%s: no %s elements\n",
554c16b380fSderaadt 				    changer_name, description);
555c16b380fSderaadt 				return (0);
556c16b380fSderaadt 			}
557c16b380fSderaadt 		}
558c16b380fSderaadt 
559c16b380fSderaadt 		bzero(&cmd, sizeof(cmd));
560c16b380fSderaadt 
561*c4a732d1Sbeck 		cmd.cesr_type = chet;
562*c4a732d1Sbeck 		/* Allocate storage for the status info. */
563*c4a732d1Sbeck 		cmd.cesr_data = calloc(count, sizeof(*cmd.cesr_data));
564*c4a732d1Sbeck 		if ((cmd.cesr_data) == NULL)
565*c4a732d1Sbeck 			errx(1, "can't allocate status storage");
566*c4a732d1Sbeck 		if (avoltag || pvoltag)
567*c4a732d1Sbeck 			cmd.cesr_flags |= CESR_VOLTAGS;
568c16b380fSderaadt 
569496bd962Sderaadt 		if (ioctl(changer_fd, CHIOGSTATUS, &cmd)) {
570*c4a732d1Sbeck 			free(cmd.cesr_data);
571c16b380fSderaadt 			err(1, "%s: CHIOGSTATUS", changer_name);
572c16b380fSderaadt 		}
573c16b380fSderaadt 
574c16b380fSderaadt 		/* Dump the status for each element of this type. */
575c16b380fSderaadt 		for (i = 0; i < count; ++i) {
576*c4a732d1Sbeck 			struct changer_element_status *ces =
577*c4a732d1Sbeck 			         &(cmd.cesr_data[i]);
578*c4a732d1Sbeck 			printf("%s %d: %s", description, i,
579*c4a732d1Sbeck 			    bits_to_string(ces->ces_flags, CESTATUS_BITS));
580*c4a732d1Sbeck 			if (pvoltag)
581*c4a732d1Sbeck 				printf(" voltag: <%s:%d>",
582*c4a732d1Sbeck 				       ces->ces_pvoltag.cv_volid,
583*c4a732d1Sbeck 				       ces->ces_pvoltag.cv_serial);
584*c4a732d1Sbeck 			if (avoltag)
585*c4a732d1Sbeck 				printf(" avoltag: <%s:%d>",
586*c4a732d1Sbeck 				       ces->ces_avoltag.cv_volid,
587*c4a732d1Sbeck 				       ces->ces_avoltag.cv_serial);
588*c4a732d1Sbeck 			printf("\n");
589c16b380fSderaadt 		}
590c16b380fSderaadt 
591*c4a732d1Sbeck 		free(cmd.cesr_data);
592c16b380fSderaadt 	}
593c16b380fSderaadt 
594c16b380fSderaadt 	return (0);
595c16b380fSderaadt 
596c16b380fSderaadt  usage:
597c16b380fSderaadt 	fprintf(stderr, "usage: %s %s [<element type>]\n", __progname,
598c16b380fSderaadt 	    cname);
599c16b380fSderaadt 	return (1);
600c16b380fSderaadt }
601c16b380fSderaadt 
602c16b380fSderaadt static int
603ab83b6d6Sderaadt parse_element_type(char *cp)
604c16b380fSderaadt {
605c16b380fSderaadt 	int i;
606c16b380fSderaadt 
607c16b380fSderaadt 	for (i = 0; elements[i].et_name != NULL; ++i)
608c16b380fSderaadt 		if (strcmp(elements[i].et_name, cp) == 0)
609c16b380fSderaadt 			return (elements[i].et_type);
610c16b380fSderaadt 
611c16b380fSderaadt 	errx(1, "invalid element type `%s'", cp);
612c16b380fSderaadt }
613c16b380fSderaadt 
614c16b380fSderaadt static int
615ab83b6d6Sderaadt parse_element_unit(char *cp)
616c16b380fSderaadt {
617c16b380fSderaadt 	int i;
618c16b380fSderaadt 	char *p;
619c16b380fSderaadt 
620c16b380fSderaadt 	i = (int)strtol(cp, &p, 10);
621c16b380fSderaadt 	if ((i < 0) || (*p != '\0'))
622c16b380fSderaadt 		errx(1, "invalid unit number `%s'", cp);
623c16b380fSderaadt 
624c16b380fSderaadt 	return (i);
625c16b380fSderaadt }
626c16b380fSderaadt 
627c16b380fSderaadt static int
628ab83b6d6Sderaadt parse_special(char *cp)
629c16b380fSderaadt {
630c16b380fSderaadt 	int val;
631c16b380fSderaadt 
632c16b380fSderaadt 	val = is_special(cp);
633c16b380fSderaadt 	if (val)
634c16b380fSderaadt 		return (val);
635c16b380fSderaadt 
636c16b380fSderaadt 	errx(1, "invalid modifier `%s'", cp);
637c16b380fSderaadt }
638c16b380fSderaadt 
639c16b380fSderaadt static int
640ab83b6d6Sderaadt is_special(char *cp)
641c16b380fSderaadt {
642c16b380fSderaadt 	int i;
643c16b380fSderaadt 
644c16b380fSderaadt 	for (i = 0; specials[i].sw_name != NULL; ++i)
645c16b380fSderaadt 		if (strcmp(specials[i].sw_name, cp) == 0)
646c16b380fSderaadt 			return (specials[i].sw_value);
647c16b380fSderaadt 
648c16b380fSderaadt 	return (0);
649c16b380fSderaadt }
650c16b380fSderaadt 
651c16b380fSderaadt static char *
652ab83b6d6Sderaadt bits_to_string(int v, const char *cp)
653c16b380fSderaadt {
654c16b380fSderaadt 	const char *np;
655c16b380fSderaadt 	char f, sep, *bp;
656c16b380fSderaadt 	static char buf[128];
657c16b380fSderaadt 
658c16b380fSderaadt 	bp = buf;
659c16b380fSderaadt 	bzero(buf, sizeof(buf));
660c16b380fSderaadt 
661c16b380fSderaadt 	for (sep = '<'; (f = *cp++) != 0; cp = np) {
662c16b380fSderaadt 		for (np = cp; *np >= ' ';)
663c16b380fSderaadt 			np++;
664c16b380fSderaadt 		if ((v & (1 << (f - 1))) == 0)
665c16b380fSderaadt 			continue;
6662f61c7c6Smillert 		(void)snprintf(bp, sizeof(buf) - (bp - &buf[0]),
667707c1213Sart 		    "%c%.*s", sep, (int)(np - cp), cp);
6682f61c7c6Smillert 		bp += strlen(bp);
669c16b380fSderaadt 		sep = ',';
670c16b380fSderaadt 	}
671c16b380fSderaadt 	if (sep != '<')
672c16b380fSderaadt 		*bp = '>';
673c16b380fSderaadt 
674c16b380fSderaadt 	return (buf);
675c16b380fSderaadt }
676c16b380fSderaadt 
677c16b380fSderaadt static void
678ab83b6d6Sderaadt usage(void)
679c16b380fSderaadt {
680b13cd761Sderaadt 	int i;
681c16b380fSderaadt 
682*c4a732d1Sbeck 	fprintf(stderr, "usage: %s [-avV] [-f device] command [args ...]\n",
683b13cd761Sderaadt 	    __progname);
684b13cd761Sderaadt 	fprintf(stderr, "commands:");
685b13cd761Sderaadt 	for (i = 0; commands[i].cc_name; i++)
686b13cd761Sderaadt 		fprintf(stderr, " %s", commands[i].cc_name);
687b13cd761Sderaadt 	fprintf(stderr, "\n");
688c16b380fSderaadt 	exit(1);
689c16b380fSderaadt }
690