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