xref: /freebsd/bin/chio/chio.c (revision ce834215a70ff69e7e222827437116eee2f9ac6f)
1 /*	$Id: chio.c,v 1.2 1997/06/02 06:25:19 charnier Exp $	*/
2 
3 /*
4  * Copyright (c) 1996 Jason R. Thorpe <thorpej@and.com>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgements:
17  *	This product includes software developed by Jason R. Thorpe
18  *	for And Communications, http://www.and.com/
19  * 4. The name of the author may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include <sys/param.h>
36 #include <sys/ioctl.h>
37 #include <sys/chio.h>
38 #include <err.h>
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <limits.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46 
47 #include "defs.h"
48 #include "pathnames.h"
49 
50 static	void usage __P((void));
51 static	void cleanup __P((void));
52 static	int parse_element_type __P((char *));
53 static	int parse_element_unit __P((char *));
54 static	int parse_special __P((char *));
55 static	int is_special __P((char *));
56 static	char *bits_to_string __P((int, const char *));
57 
58 static	int do_move __P((char *, int, char **));
59 static	int do_exchange __P((char *, int, char **));
60 static	int do_position __P((char *, int, char **));
61 static	int do_params __P((char *, int, char **));
62 static	int do_getpicker __P((char *, int, char **));
63 static	int do_setpicker __P((char *, int, char **));
64 static	int do_status __P((char *, int, char **));
65 
66 /* Valid changer element types. */
67 const struct element_type elements[] = {
68 	{ "picker",		CHET_MT },
69 	{ "slot",		CHET_ST },
70 	{ "portal",		CHET_IE },
71 	{ "drive",		CHET_DT },
72 	{ NULL,			0 },
73 };
74 
75 /* Valid commands. */
76 const struct changer_command commands[] = {
77 	{ "move",		do_move },
78 	{ "exchange",		do_exchange },
79 	{ "position",		do_position },
80 	{ "params",		do_params },
81 	{ "getpicker",		do_getpicker },
82 	{ "setpicker",		do_setpicker },
83 	{ "status",		do_status },
84 	{ NULL,			0 },
85 };
86 
87 /* Valid special words. */
88 const struct special_word specials[] = {
89 	{ "inv",		SW_INVERT },
90 	{ "inv1",		SW_INVERT1 },
91 	{ "inv2",		SW_INVERT2 },
92 	{ NULL,			0 },
93 };
94 
95 static	int changer_fd;
96 static	char *changer_name;
97 
98 int
99 main(argc, argv)
100 	int argc;
101 	char **argv;
102 {
103 	int ch, i;
104 
105 	while ((ch = getopt(argc, argv, "f:")) != -1) {
106 		switch (ch) {
107 		case 'f':
108 			changer_name = optarg;
109 			break;
110 
111 		default:
112 			usage();
113 		}
114 	}
115 	argc -= optind;
116 	argv += optind;
117 
118 	if (argc == 0)
119 		usage();
120 
121 	/* Get the default changer if not already specified. */
122 	if (changer_name == NULL)
123 		if ((changer_name = getenv(CHANGER_ENV_VAR)) == NULL)
124 			changer_name = _PATH_CH;
125 
126 	/* Open the changer device. */
127 	if ((changer_fd = open(changer_name, O_RDWR, 0600)) == -1)
128 		err(1, "%s: open", changer_name);
129 
130 	/* Register cleanup function. */
131 	if (atexit(cleanup))
132 		err(1, "can't register cleanup function");
133 
134 	/* Find the specified command. */
135 	for (i = 0; commands[i].cc_name != NULL; ++i)
136 		if (strcmp(*argv, commands[i].cc_name) == 0)
137 			break;
138 	if (commands[i].cc_name == NULL)
139 		errx(1, "unknown command: %s", *argv);
140 
141 	/* Skip over the command name and call handler. */
142 	++argv; --argc;
143 	exit ((*commands[i].cc_handler)(commands[i].cc_name, argc, argv));
144 }
145 
146 static int
147 do_move(cname, argc, argv)
148 	char *cname;
149 	int argc;
150 	char **argv;
151 {
152 	struct changer_move cmd;
153 	int val;
154 
155 	/*
156 	 * On a move command, we expect the following:
157 	 *
158 	 * <from ET> <from EU> <to ET> <to EU> [inv]
159 	 *
160 	 * where ET == element type and EU == element unit.
161 	 */
162 	if (argc < 4) {
163 		warnx("%s: too few arguments", cname);
164 		goto usage;
165 	} else if (argc > 5) {
166 		warnx("%s: too many arguments", cname);
167 		goto usage;
168 	}
169 	bzero(&cmd, sizeof(cmd));
170 
171 	/* <from ET>  */
172 	cmd.cm_fromtype = parse_element_type(*argv);
173 	++argv; --argc;
174 
175 	/* <from EU> */
176 	cmd.cm_fromunit = parse_element_unit(*argv);
177 	++argv; --argc;
178 
179 	/* <to ET> */
180 	cmd.cm_totype = parse_element_type(*argv);
181 	++argv; --argc;
182 
183 	/* <to EU> */
184 	cmd.cm_tounit = parse_element_unit(*argv);
185 	++argv; --argc;
186 
187 	/* Deal with optional command modifier. */
188 	if (argc) {
189 		val = parse_special(*argv);
190 		switch (val) {
191 		case SW_INVERT:
192 			cmd.cm_flags |= CM_INVERT;
193 			break;
194 
195 		default:
196 			errx(1, "%s: inappropriate modifier `%s'",
197 			    cname, *argv);
198 			/* NOTREACHED */
199 		}
200 	}
201 
202 	/* Send command to changer. */
203 	if (ioctl(changer_fd, CHIOMOVE, (char *)&cmd))
204 		err(1, "%s: CHIOMOVE", changer_name);
205 
206 	return (0);
207 
208  usage:
209 	fprintf(stderr, "usage: chio %s "
210 	    "<from ET> <from EU> <to ET> <to EU> [inv]\n", cname);
211 	return (1);
212 }
213 
214 static int
215 do_exchange(cname, argc, argv)
216 	char *cname;
217 	int argc;
218 	char **argv;
219 {
220 	struct changer_exchange cmd;
221 	int val;
222 
223 	/*
224 	 * On an exchange command, we expect the following:
225 	 *
226   * <src ET> <src EU> <dst1 ET> <dst1 EU> [<dst2 ET> <dst2 EU>] [inv1] [inv2]
227 	 *
228 	 * where ET == element type and EU == element unit.
229 	 */
230 	if (argc < 4) {
231 		warnx("%s: too few arguments", cname);
232 		goto usage;
233 	} else if (argc > 8) {
234 		warnx("%s: too many arguments", cname);
235 		goto usage;
236 	}
237 	bzero(&cmd, sizeof(cmd));
238 
239 	/* <src ET>  */
240 	cmd.ce_srctype = parse_element_type(*argv);
241 	++argv; --argc;
242 
243 	/* <src EU> */
244 	cmd.ce_srcunit = parse_element_unit(*argv);
245 	++argv; --argc;
246 
247 	/* <dst1 ET> */
248 	cmd.ce_fdsttype = parse_element_type(*argv);
249 	++argv; --argc;
250 
251 	/* <dst1 EU> */
252 	cmd.ce_fdstunit = parse_element_unit(*argv);
253 	++argv; --argc;
254 
255 	/*
256 	 * If the next token is a special word or there are no more
257 	 * arguments, then this is a case of simple exchange.
258 	 * dst2 == src.
259 	 */
260 	if ((argc == 0) || is_special(*argv)) {
261 		cmd.ce_sdsttype = cmd.ce_srctype;
262 		cmd.ce_sdstunit = cmd.ce_srcunit;
263 		goto do_special;
264 	}
265 
266 	/* <dst2 ET> */
267 	cmd.ce_sdsttype = parse_element_type(*argv);
268 	++argv; --argc;
269 
270 	/* <dst2 EU> */
271 	cmd.ce_sdstunit = parse_element_unit(*argv);
272 	++argv; --argc;
273 
274  do_special:
275 	/* Deal with optional command modifiers. */
276 	while (argc) {
277 		val = parse_special(*argv);
278 		++argv; --argc;
279 		switch (val) {
280 		case SW_INVERT1:
281 			cmd.ce_flags |= CE_INVERT1;
282 			break;
283 
284 		case SW_INVERT2:
285 			cmd.ce_flags |= CE_INVERT2;
286 			break;
287 
288 		default:
289 			errx(1, "%s: inappropriate modifier `%s'",
290 			    cname, *argv);
291 			/* NOTREACHED */
292 		}
293 	}
294 
295 	/* Send command to changer. */
296 	if (ioctl(changer_fd, CHIOEXCHANGE, (char *)&cmd))
297 		err(1, "%s: CHIOEXCHANGE", changer_name);
298 
299 	return (0);
300 
301  usage:
302 	fprintf(stderr,
303 		"usage: chio %s <src ET> <src EU> <dst1 ET> <dst1 EU>\n"
304 		"       [<dst2 ET> <dst2 EU>] [inv1] [inv2]\n", cname);
305 	return (1);
306 }
307 
308 static int
309 do_position(cname, argc, argv)
310 	char *cname;
311 	int argc;
312 	char **argv;
313 {
314 	struct changer_position cmd;
315 	int val;
316 
317 	/*
318 	 * On a position command, we expect the following:
319 	 *
320 	 * <to ET> <to EU> [inv]
321 	 *
322 	 * where ET == element type and EU == element unit.
323 	 */
324 	if (argc < 2) {
325 		warnx("%s: too few arguments", cname);
326 		goto usage;
327 	} else if (argc > 3) {
328 		warnx("%s: too many arguments", cname);
329 		goto usage;
330 	}
331 	bzero(&cmd, sizeof(cmd));
332 
333 	/* <to ET>  */
334 	cmd.cp_type = parse_element_type(*argv);
335 	++argv; --argc;
336 
337 	/* <to EU> */
338 	cmd.cp_unit = parse_element_unit(*argv);
339 	++argv; --argc;
340 
341 	/* Deal with optional command modifier. */
342 	if (argc) {
343 		val = parse_special(*argv);
344 		switch (val) {
345 		case SW_INVERT:
346 			cmd.cp_flags |= CP_INVERT;
347 			break;
348 
349 		default:
350 			errx(1, "%s: inappropriate modifier `%s'",
351 			    cname, *argv);
352 			/* NOTREACHED */
353 		}
354 	}
355 
356 	/* Send command to changer. */
357 	if (ioctl(changer_fd, CHIOPOSITION, (char *)&cmd))
358 		err(1, "%s: CHIOPOSITION", changer_name);
359 
360 	return (0);
361 
362  usage:
363 	fprintf(stderr, "usage: chio %s <to ET> <to EU> [inv]\n", cname);
364 	return (1);
365 }
366 
367 static int
368 do_params(cname, argc, argv)
369 	char *cname;
370 	int argc;
371 	char **argv;
372 {
373 	struct changer_params data;
374 
375 	/* No arguments to this command. */
376 	if (argc) {
377 		warnx("%s: no arguements expected", cname);
378 		goto usage;
379 	}
380 
381 	/* Get params from changer and display them. */
382 	bzero(&data, sizeof(data));
383 	if (ioctl(changer_fd, CHIOGPARAMS, (char *)&data))
384 		err(1, "%s: CHIOGPARAMS", changer_name);
385 
386 	printf("%s: %d slot%s, %d drive%s, %d picker%s",
387 	    changer_name,
388 	    data.cp_nslots, (data.cp_nslots > 1) ? "s" : "",
389 	    data.cp_ndrives, (data.cp_ndrives > 1) ? "s" : "",
390 	    data.cp_npickers, (data.cp_npickers > 1) ? "s" : "");
391 	if (data.cp_nportals)
392 		printf(", %d portal%s", data.cp_nportals,
393 		    (data.cp_nportals > 1) ? "s" : "");
394 	printf("\n%s: current picker: %d\n", changer_name, data.cp_curpicker);
395 
396 	return (0);
397 
398  usage:
399 	fprintf(stderr, "usage: chio %s\n", cname);
400 	return (1);
401 }
402 
403 static int
404 do_getpicker(cname, argc, argv)
405 	char *cname;
406 	int argc;
407 	char **argv;
408 {
409 	int picker;
410 
411 	/* No arguments to this command. */
412 	if (argc) {
413 		warnx("%s: no arguments expected", cname);
414 		goto usage;
415 	}
416 
417 	/* Get current picker from changer and display it. */
418 	if (ioctl(changer_fd, CHIOGPICKER, (char *)&picker))
419 		err(1, "%s: CHIOGPICKER", changer_name);
420 
421 	printf("%s: current picker: %d\n", changer_name, picker);
422 
423 	return (0);
424 
425  usage:
426 	fprintf(stderr, "usage: chio %s\n", cname);
427 	return (1);
428 }
429 
430 static int
431 do_setpicker(cname, argc, argv)
432 	char *cname;
433 	int argc;
434 	char **argv;
435 {
436 	int picker;
437 
438 	if (argc < 1) {
439 		warnx("%s: too few arguments", cname);
440 		goto usage;
441 	} else if (argc > 1) {
442 		warnx("%s: too many arguments", cname);
443 		goto usage;
444 	}
445 
446 	picker = parse_element_unit(*argv);
447 
448 	/* Set the changer picker. */
449 	if (ioctl(changer_fd, CHIOSPICKER, (char *)&picker))
450 		err(1, "%s: CHIOSPICKER", changer_name);
451 
452 	return (0);
453 
454  usage:
455 	fprintf(stderr, "usage: chio %s <picker>\n", cname);
456 	return (1);
457 }
458 
459 static int
460 do_status(cname, argc, argv)
461 	char *cname;
462 	int argc;
463 	char **argv;
464 {
465 	struct changer_element_status cmd;
466 	struct changer_params data;
467 	u_int8_t *statusp;
468 	int i, count, chet, schet, echet;
469 	char *description;
470 
471 	count = 0;
472 	description = NULL;
473 
474 	/*
475 	 * On a status command, we expect the following:
476 	 *
477 	 * [<ET>]
478 	 *
479 	 * where ET == element type.
480 	 *
481 	 * If we get no arguments, we get the status of all
482 	 * known element types.
483 	 */
484 	if (argc > 1) {
485 		warnx("%s: too many arguments", cname);
486 		goto usage;
487 	}
488 
489 	/*
490 	 * Get params from changer.  Specifically, we need the element
491 	 * counts.
492 	 */
493 	bzero(&data, sizeof(data));
494 	if (ioctl(changer_fd, CHIOGPARAMS, (char *)&data))
495 		err(1, "%s: CHIOGPARAMS", changer_name);
496 
497 	if (argc)
498 		schet = echet = parse_element_type(*argv);
499 	else {
500 		schet = CHET_MT;
501 		echet = CHET_DT;
502 	}
503 
504 	for (chet = schet; chet <= echet; ++chet) {
505 		switch (chet) {
506 		case CHET_MT:
507 			count = data.cp_npickers;
508 			description = "picker";
509 			break;
510 
511 		case CHET_ST:
512 			count = data.cp_nslots;
513 			description = "slot";
514 			break;
515 
516 		case CHET_IE:
517 			count = data.cp_nportals;
518 			description = "portal";
519 			break;
520 
521 		case CHET_DT:
522 			count = data.cp_ndrives;
523 			description = "drive";
524 			break;
525 		}
526 
527 		if (count == 0) {
528 			if (argc == 0)
529 				continue;
530 			else {
531 				printf("%s: no %s elements\n",
532 				    changer_name, description);
533 				return (0);
534 			}
535 		}
536 
537 		/* Allocate storage for the status bytes. */
538 		if ((statusp = (u_int8_t *)malloc(count)) == NULL)
539 			errx(1, "can't allocate status storage");
540 
541 		bzero(statusp, count);
542 		bzero(&cmd, sizeof(cmd));
543 
544 		cmd.ces_type = chet;
545 		cmd.ces_data = statusp;
546 
547 		if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cmd)) {
548 			free(statusp);
549 			err(1, "%s: CHIOGSTATUS", changer_name);
550 		}
551 
552 		/* Dump the status for each element of this type. */
553 		for (i = 0; i < count; ++i) {
554 			printf("%s %d: %s\n", description, i,
555 			    bits_to_string(statusp[i], CESTATUS_BITS));
556 		}
557 
558 		free(statusp);
559 	}
560 
561 	return (0);
562 
563  usage:
564 	fprintf(stderr, "usage: chio %s [<element type>]\n", cname);
565 	return (1);
566 }
567 
568 static int
569 parse_element_type(cp)
570 	char *cp;
571 {
572 	int i;
573 
574 	for (i = 0; elements[i].et_name != NULL; ++i)
575 		if (strcmp(elements[i].et_name, cp) == 0)
576 			return (elements[i].et_type);
577 
578 	errx(1, "invalid element type `%s'", cp);
579 }
580 
581 static int
582 parse_element_unit(cp)
583 	char *cp;
584 {
585 	int i;
586 	char *p;
587 
588 	i = (int)strtol(cp, &p, 10);
589 	if ((i < 0) || (*p != '\0'))
590 		errx(1, "invalid unit number `%s'", cp);
591 
592 	return (i);
593 }
594 
595 static int
596 parse_special(cp)
597 	char *cp;
598 {
599 	int val;
600 
601 	val = is_special(cp);
602 	if (val)
603 		return (val);
604 
605 	errx(1, "invalid modifier `%s'", cp);
606 }
607 
608 static int
609 is_special(cp)
610 	char *cp;
611 {
612 	int i;
613 
614 	for (i = 0; specials[i].sw_name != NULL; ++i)
615 		if (strcmp(specials[i].sw_name, cp) == 0)
616 			return (specials[i].sw_value);
617 
618 	return (0);
619 }
620 
621 static char *
622 bits_to_string(v, cp)
623 	int v;
624 	const char *cp;
625 {
626 	const char *np;
627 	char f, sep, *bp;
628 	static char buf[128];
629 
630 	bp = buf;
631 	bzero(buf, sizeof(buf));
632 
633 	for (sep = '<'; (f = *cp++) != 0; cp = np) {
634 		for (np = cp; *np >= ' ';)
635 			np++;
636 		if ((v & (1 << (f - 1))) == 0)
637 			continue;
638 		bp += snprintf(bp, sizeof(buf) - (bp - &buf[0]),
639 						"%c%.*s", sep, np - cp, cp);
640 		sep = ',';
641 	}
642 	if (sep != '<')
643 		*bp = '>';
644 
645 	return (buf);
646 }
647 
648 static void
649 cleanup()
650 {
651 
652 	/* Simple enough... */
653 	(void)close(changer_fd);
654 }
655 
656 static void
657 usage()
658 {
659 	int i;
660 
661 	fprintf(stderr, "usage: chio [-f changer] command [args ...]\n");
662 	fprintf(stderr, "commands:");
663 	for (i = 0; commands[i].cc_name; i++)
664 		fprintf(stderr, " %s", commands[i].cc_name);
665 	fprintf(stderr, "\n");
666 	exit(1);
667 }
668