xref: /openbsd-src/sbin/scsi/scsi.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: scsi.c,v 1.25 2007/10/18 00:30:32 ray Exp $	*/
2 /*	$FreeBSD: scsi.c,v 1.11 1996/04/06 11:00:28 joerg Exp $	*/
3 
4 /*
5  * Written By Julian ELischer
6  * Copyright julian Elischer 1993.
7  * Permission is granted to use or redistribute this file in any way as long
8  * as this notice remains. Julian Elischer does not guarantee that this file
9  * is totally correct for any given task and users of this file must
10  * accept responsibility for any damage that occurs from the application of this
11  * file.
12  *
13  * (julian@tfs.com julian@dialix.oz.au)
14  *
15  * User SCSI hooks added by Peter Dufault:
16  *
17  * Copyright (c) 1994 HD Associates
18  * (contact: dufault@hda.com)
19  * All rights reserved.
20  *
21  * Redistribution and use in source and binary forms, with or without
22  * modification, are permitted provided that the following conditions
23  * are met:
24  * 1. Redistributions of source code must retain the above copyright
25  *    notice, this list of conditions and the following disclaimer.
26  * 2. Redistributions in binary form must reproduce the above copyright
27  *    notice, this list of conditions and the following disclaimer in the
28  *    documentation and/or other materials provided with the distribution.
29  * 3. The name of HD Associates
30  *    may not be used to endorse or promote products derived from this software
31  *    without specific prior written permission.
32  *
33  * THIS SOFTWARE IS PROVIDED BY HD ASSOCIATES ``AS IS'' AND
34  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
35  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
36  * ARE DISCLAIMED.  IN NO EVENT SHALL HD ASSOCIATES BE LIABLE
37  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
38  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
39  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
41  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
42  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
43  * SUCH DAMAGE.
44  */
45 
46 #include <sys/types.h>
47 #include <sys/wait.h>
48 
49 #include <stdio.h>
50 #include <string.h>
51 #include <stdlib.h>
52 #include <unistd.h>
53 #include <errno.h>
54 #include <sys/scsiio.h>
55 #include <sys/file.h>
56 #include <ctype.h>
57 #include <signal.h>
58 #include <err.h>
59 #include <paths.h>
60 
61 #include "libscsi.h"
62 
63 int	fd;
64 int	debuglevel;
65 int	debugflag;
66 int commandflag;
67 int verbose = 0;
68 
69 int modeflag;
70 int editflag;
71 int modepage = 0; /* Read this mode page */
72 int pagectl = 0;  /* Mode sense page control */
73 int seconds = 2;
74 
75 void	procargs(int *argc_p, char ***argv_p);
76 int	iget(void *hook, char *name);
77 char	*cget(void *hook, char *name);
78 void	arg_put(void *hook, int letter, void *arg, int count, char *name);
79 void	mode_sense(int fd, u_char *data, int len, int pc, int page);
80 void	mode_select(int fd, u_char *data, int len, int perm);
81 
82 static void
83 usage(void)
84 {
85 	fprintf(stderr,
86 "Usage:\n"
87 "\n"
88 "  scsi -f device -d debug_level                    # To set debug level\n"
89 "  scsi -f device -m page [-P pc]                   # To read mode pages\n"
90 "  scsi -f device [-v] [-s seconds] -c cmd_fmt [arg0 ... argn] # A command...\n"
91 "                 -o count out_fmt [arg0 ... argn]  #   EITHER (data out)\n"
92 "                 -i count in_fmt                   #   OR     (data in)\n"
93 "\n"
94 "\"out_fmt\" can be \"-\" to read output data from stdin;\n"
95 "\"in_fmt\" can be \"-\" to write input data to stdout;\n"
96 "\n"
97 "If debugging is not compiled in the kernel, \"-d\" will have no effect\n"
98 
99 );
100 
101 	exit (1);
102 }
103 
104 void
105 procargs(int *argc_p, char ***argv_p)
106 {
107 	int argc = *argc_p;
108 	char **argv = *argv_p;
109 	int fflag, ch;
110 
111 	fflag = 0;
112 	commandflag = 0;
113 	debugflag = 0;
114 	while ((ch = getopt(argc, argv, "cef:d:m:P:s:v")) != -1) {
115 		switch (ch) {
116 		case 'c':
117 			commandflag = 1;
118 			break;
119 		case 'e':
120 			editflag = 1;
121 			break;
122 		case 'f':
123 			if ((fd = scsi_open(optarg, O_RDWR)) < 0)
124 				err(errno, "unable to open device %s", optarg);
125 			fflag = 1;
126 			break;
127 		case 'd':
128 			debuglevel = strtol(optarg, 0, 0);
129 			debugflag = 1;
130 			break;
131 		case 'm':
132 			modeflag = 1;
133 			modepage = strtol(optarg, 0, 0);
134 			break;
135 		case 'P':
136 			pagectl = strtol(optarg, 0, 0);
137 			break;
138 		case 's':
139 			seconds = strtol(optarg, 0, 0);
140 			break;
141 		case 'v':
142 			verbose = 1;
143 			break;
144 		case '?':
145 		default:
146 			usage();
147 		}
148 	}
149 	*argc_p = argc - optind;
150 	*argv_p = argv + optind;
151 
152 	if (!fflag) usage();
153 }
154 
155 /* get_hook: Structure for evaluating args in a callback.
156  */
157 struct get_hook
158 {
159 	int argc;
160 	char **argv;
161 	int got;
162 };
163 
164 /* iget: Integer argument callback
165  */
166 int
167 iget(void *hook, char *name)
168 {
169 	struct get_hook *h = (struct get_hook *)hook;
170 	int arg;
171 
172 	if (h->got >= h->argc)
173 	{
174 		fprintf(stderr, "Expecting an integer argument.\n");
175 		usage();
176 	}
177 	arg = strtol(h->argv[h->got], 0, 0);
178 	h->got++;
179 
180 	if (verbose && name && *name)
181 		printf("%s: %d\n", name, arg);
182 
183 	return arg;
184 }
185 
186 /* cget: char * argument callback
187  */
188 char *
189 cget(void *hook, char *name)
190 {
191 	struct get_hook *h = (struct get_hook *)hook;
192 	char *arg;
193 
194 	if (h->got >= h->argc)
195 	{
196 		fprintf(stderr, "Expecting a character pointer argument.\n");
197 		usage();
198 	}
199 	arg = h->argv[h->got];
200 	h->got++;
201 
202 	if (verbose && name)
203 		printf("cget: %s: %s", name, arg);
204 
205 	return arg;
206 }
207 
208 /* arg_put: "put argument" callback
209  */
210 void arg_put(void *hook, int letter, void *arg, int count, char *name)
211 {
212 	if (verbose && name && *name)
213 		printf("%s:  ", name);
214 
215 	switch(letter)
216 	{
217 		case 'i':
218 		case 'b':
219 		printf("%ld ", (long)arg);
220 		break;
221 
222 		case 'c':
223 		case 'z':
224 		{
225 			char *p = malloc(count + 1);
226 			p[count] = 0;
227 			strncpy(p, (char *)arg, count);
228 			if (letter == 'z')
229 			{
230 				int i;
231 				for (i = count - 1; i >= 0; i--)
232 					if (p[i] == ' ')
233 						p[i] = 0;
234 					else
235 						break;
236 			}
237 			printf("%s ", p);
238 		}
239 
240 		break;
241 
242 		default:
243 		printf("Unknown format letter: '%c'\n", letter);
244 	}
245 	if (verbose)
246 		putchar('\n');
247 }
248 
249 /* data_phase: SCSI bus data phase: DATA IN, DATA OUT, or no data transfer.
250  */
251 enum data_phase {none = 0, in, out};
252 
253 /* do_cmd: Send a command to a SCSI device
254  */
255 static void
256 do_cmd(int fd, char *fmt, int argc, char **argv)
257 {
258 	struct get_hook h;
259 	scsireq_t *scsireq = scsireq_new();
260 	enum data_phase data_phase;
261 	int count, amount;
262 	char *data_fmt, *bp;
263 
264 	h.argc = argc;
265 	h.argv = argv;
266 	h.got = 0;
267 
268 	scsireq_reset(scsireq);
269 
270 	scsireq_build_visit(scsireq, 0, 0, 0, fmt, iget, (void *)&h);
271 
272 	/* Three choices here:
273 	 * 1. We've used up all the args and have no data phase.
274 	 * 2. We have input data ("-i")
275 	 * 3. We have output data ("-o")
276 	 */
277 
278 	if (h.got >= h.argc)
279 	{
280 		data_phase = none;
281 		count = scsireq->datalen = 0;
282 	}
283 	else
284 	{
285 		char *flag = cget(&h, 0);
286 
287 		if (strcmp(flag, "-o") == 0)
288 		{
289 			data_phase = out;
290 			scsireq->flags = SCCMD_WRITE;
291 		}
292 		else if (strcmp(flag, "-i") == 0)
293 		{
294 			data_phase = in;
295 			scsireq->flags = SCCMD_READ;
296 		}
297 		else
298 		{
299 			fprintf(stderr,
300 			"Need either \"-i\" or \"-o\" for data phase; not \"%s\".\n", flag);
301 			usage();
302 		}
303 
304 		count = scsireq->datalen = iget(&h, 0);
305 		if (count) {
306 			data_fmt = cget(&h, 0);
307 
308 			scsireq->databuf = malloc(count);
309 
310 			if (data_phase == out) {
311 				if (strcmp(data_fmt, "-") == 0)	{
312 					bp = (char *)scsireq->databuf;
313 					while (count > 0 &&
314 					    (amount = read(STDIN_FILENO,
315 					    bp, count)) > 0) {
316 						count -= amount;
317 						bp += amount;
318 					}
319 					if (amount == -1)
320 						err(errno, "read");
321 					else if (amount == 0) {
322 						/* early EOF */
323 						fprintf(stderr,
324 							"Warning: only read %lu bytes out of %lu.\n",
325 							scsireq->datalen - (u_long)count,
326 							scsireq->datalen);
327 						scsireq->datalen -= (u_long)count;
328 					}
329 				}
330 				else
331 				{
332 					bzero(scsireq->databuf, count);
333 					scsireq_encode_visit(scsireq, data_fmt, iget, (void *)&h);
334 				}
335 			}
336 		}
337 	}
338 
339 
340 	scsireq->timeout = seconds * 1000;
341 
342 	if (scsireq_enter(fd, scsireq) == -1)
343 	{
344 		scsi_debug(stderr, -1, scsireq);
345 		exit(errno);
346 	}
347 
348 	if (SCSIREQ_ERROR(scsireq))
349 		scsi_debug(stderr, 0, scsireq);
350 
351 	if (count && data_phase == in)
352 	{
353 		if (strcmp(data_fmt, "-") == 0)	/* stdout */
354 		{
355 			bp = (char *)scsireq->databuf;
356 			while (count > 0 && (amount = write(STDOUT_FILENO, bp, count)) > 0)
357 			{
358 				count -= amount;
359 				bp += amount;
360 			}
361 			if (amount < 0)
362 				err(errno, "write");
363 			else if (amount == 0)
364 				fprintf(stderr, "Warning: wrote only %lu bytes out of %lu.\n",
365 					scsireq->datalen - count,
366 					scsireq->datalen);
367 
368 		}
369 		else
370 		{
371 			scsireq_decode_visit(scsireq, data_fmt, arg_put, 0);
372 			putchar('\n');
373 		}
374 	}
375 }
376 
377 void mode_sense(int fd, u_char *data, int len, int pc, int page)
378 {
379 	scsireq_t *scsireq;
380 
381 	bzero(data, len);
382 
383 	scsireq = scsireq_new();
384 
385 	if (scsireq_enter(fd, scsireq_build(scsireq,
386 	 len, data, SCCMD_READ,
387 	 "1A 0 v:2 {Page Control} v:6 {Page Code} 0 v:i1 {Allocation Length} 0",
388 	 pc, page, len)) == -1)	/* Mode sense */
389 	{
390 		scsi_debug(stderr, -1, scsireq);
391 		exit(errno);
392 	}
393 
394 	if (SCSIREQ_ERROR(scsireq))
395 	{
396 		scsi_debug(stderr, 0, scsireq);
397 		exit(1);
398 	}
399 
400 	free(scsireq);
401 }
402 
403 void mode_select(int fd, u_char *data, int len, int perm)
404 {
405 	scsireq_t *scsireq;
406 
407 	scsireq = scsireq_new();
408 
409 	if (scsireq_enter(fd, scsireq_build(scsireq,
410 	 len, data, SCCMD_WRITE,
411 	 "15 0:7 v:1 {SP} 0 0 v:i1 {Allocation Length} 0", perm, len)) == -1)	/* Mode select */
412 	{
413 		scsi_debug(stderr, -1, scsireq);
414 		exit(errno);
415 	}
416 
417 	if (SCSIREQ_ERROR(scsireq))
418 	{
419 		scsi_debug(stderr, 0, scsireq);
420 		exit(1);
421 	}
422 
423 	free(scsireq);
424 }
425 
426 
427 #define START_ENTRY '{'
428 #define END_ENTRY '}'
429 
430 static void
431 skipwhite(FILE *f)
432 {
433 	int c;
434 
435 skip_again:
436 
437 	while (isspace(c = getc(f)))
438 		;
439 
440 	if (c == '#') {
441 		while ((c = getc(f)) != '\n' && c != EOF)
442 			;
443 		goto skip_again;
444 	}
445 
446 	ungetc(c, f);
447 }
448 
449 /* mode_lookup: Lookup a format description for a given page.
450  */
451 char *mode_db = "/usr/share/misc/scsi_modes";
452 static char *mode_lookup(int page)
453 {
454 	char *new_db;
455 	FILE *modes;
456 	int match, next, found, c;
457 	static char fmt[1024];	/* XXX This should be with strealloc */
458 	int page_desc;
459 	new_db = getenv("SCSI_MODES");
460 
461 	if (new_db)
462 		mode_db = new_db;
463 
464 	modes = fopen(mode_db, "r");
465 	if (modes == NULL)
466 		return 0;
467 
468 	next = 0;
469 	found = 0;
470 
471 	while (!found) {
472 
473 		skipwhite(modes);
474 
475 		if (fscanf(modes, "%i", &page_desc) != 1)
476 			break;
477 
478 		if (page_desc == page)
479 			found = 1;
480 
481 		skipwhite(modes);
482 		if (getc(modes) != START_ENTRY) {
483 			fprintf(stderr, "Expected %c.\n", START_ENTRY);
484 			exit(1);
485 		}
486 
487 		match = 1;
488 		while (match != 0) {
489 			c = getc(modes);
490 			if (c == EOF)
491 				fprintf(stderr, "Expected %c.\n", END_ENTRY);
492 
493 			if (c == START_ENTRY) {
494 				match++;
495 			}
496 			if (c == END_ENTRY) {
497 				match--;
498 				if (match == 0)
499 					break;
500 			}
501 			if (found && c != '\n') {
502 				if (next >= sizeof(fmt)) {
503 					fprintf(stderr,
504 					    "Stupid program: Buffer overflow.\n");
505 					exit(ENOMEM);
506 				}
507 
508 				fmt[next++] = (u_char)c;
509 			}
510 		}
511 	}
512 	fclose(modes);
513 	fmt[next] = 0;
514 
515 	return (found) ? fmt : 0;
516 }
517 
518 /* -------- edit: Mode Select Editor ---------
519  */
520 struct editinfo
521 {
522 	long can_edit;
523 	long default_value;
524 } editinfo[64];	/* XXX Bogus fixed size */
525 
526 static int editind;
527 volatile int edit_opened;
528 static FILE *edit_file;
529 static char edit_name[L_tmpnam];
530 
531 static void
532 edit_rewind(void)
533 {
534 	editind = 0;
535 }
536 
537 static void
538 edit_done(void)
539 {
540 	int opened;
541 
542 	sigset_t all, prev;
543 	sigfillset(&all);
544 
545 	(void)sigprocmask(SIG_SETMASK, &all, &prev);
546 
547 	opened = (int)edit_opened;
548 	edit_opened = 0;
549 
550 	(void)sigprocmask(SIG_SETMASK, &prev, 0);
551 
552 	if (opened)
553 	{
554 		if (fclose(edit_file))
555 			perror(edit_name);
556 		if (unlink(edit_name))
557 			perror(edit_name);
558 	}
559 }
560 
561 static void
562 edit_init(void)
563 {
564 	int fd;
565 
566 	edit_rewind();
567 	strlcpy(edit_name, "/var/tmp/scXXXXXXXX", sizeof edit_name);
568 	if ((fd = mkstemp(edit_name)) == -1)
569 		err(errno, "mkstemp failed");
570 	if ( (edit_file = fdopen(fd, "w+")) == 0)
571 		err(errno, "fdopen failed");
572 	edit_opened = 1;
573 
574 	atexit(edit_done);
575 }
576 
577 static void
578 edit_check(void *hook, int letter, void *arg, int count, char *name)
579 {
580 	if (letter != 'i' && letter != 'b') {
581 		fprintf(stderr, "Can't edit format %c.\n", letter);
582 		exit(1);
583 	}
584 
585 	if (editind >= sizeof(editinfo) / sizeof(editinfo[0])) {
586 		fprintf(stderr, "edit table overflow\n");
587 		exit(ENOMEM);
588 	}
589 	editinfo[editind].can_edit = ((long)arg != 0);
590 	editind++;
591 }
592 
593 static void
594 edit_defaults(void *hook, int letter, void *arg, int count, char *name)
595 {
596 	if (letter != 'i' && letter != 'b') {
597 		fprintf(stderr, "Can't edit format %c.\n", letter);
598 		exit(1);
599 	}
600 
601 	editinfo[editind].default_value = ((long)arg);
602 	editind++;
603 }
604 
605 static void
606 edit_report(void *hook, int letter, void *arg, int count, char *name)
607 {
608 	if (editinfo[editind].can_edit) {
609 		if (letter != 'i' && letter != 'b') {
610 			fprintf(stderr, "Can't report format %c.\n", letter);
611 			exit(1);
612 		}
613 
614 		fprintf(edit_file, "%s:  %ld\n", name, (long)arg);
615 	}
616 
617 	editind++;
618 }
619 
620 static int
621 edit_get(void *hook, char *name)
622 {
623 	int arg = editinfo[editind].default_value;
624 
625 	if (editinfo[editind].can_edit) {
626 		char line[80];
627 		size_t len;
628 		if (fgets(line, sizeof(line), edit_file) == NULL)
629 			err(errno, "fgets");
630 
631 		len = strlen(line);
632 		if (len && line[len - 1] == '\n')
633 			line[len - 1] = '\0';
634 
635 		if (strncmp(name, line, strlen(name)) != 0) {
636 			fprintf(stderr, "Expected \"%s\" and read \"%s\"\n",
637 			    name, line);
638 			exit(1);
639 		}
640 
641 		arg = strtoul(line + strlen(name) + 2, 0, 0);
642 	}
643 
644 	editind++;
645 	return arg;
646 }
647 
648 int
649 editit(const char *pathname)
650 {
651 	char *argp[] = {"sh", "-c", NULL, NULL}, *ed, *p;
652 	sig_t sighup, sigint, sigquit;
653 	pid_t pid;
654 	int st;
655 
656 	ed = getenv("VISUAL");
657 	if (ed == NULL || ed[0] == '\0')
658 		ed = getenv("EDITOR");
659 	if (ed == NULL || ed[0] == '\0')
660 		ed = _PATH_VI;
661 	if (asprintf(&p, "%s %s", ed, pathname) == -1)
662 		return (-1);
663 	argp[2] = p;
664 
665  top:
666 	sighup = signal(SIGHUP, SIG_IGN);
667 	sigint = signal(SIGINT, SIG_IGN);
668 	sigquit = signal(SIGQUIT, SIG_IGN);
669 	if ((pid = fork()) == -1) {
670 		int saved_errno = errno;
671 
672 		(void)signal(SIGHUP, sighup);
673 		(void)signal(SIGINT, sigint);
674 		(void)signal(SIGQUIT, sigquit);
675 		if (saved_errno == EAGAIN) {
676 			sleep(1);
677 			goto top;
678 		}
679 		free(p);
680 		errno = saved_errno;
681 		return (-1);
682 	}
683 	if (pid == 0) {
684 		execv(_PATH_BSHELL, argp);
685 		_exit(127);
686 	}
687 	free(p);
688 	for (;;) {
689 		if (waitpid(pid, &st, 0) == -1) {
690 			if (errno != EINTR)
691 				return (-1);
692 		} else
693 			break;
694 	}
695 	(void)signal(SIGHUP, sighup);
696 	(void)signal(SIGINT, sigint);
697 	(void)signal(SIGQUIT, sigquit);
698 	if (!WIFEXITED(st) || WEXITSTATUS(st) != 0) {
699 		errno = ECHILD;
700 		return (-1);
701 	}
702 	return (0);
703 }
704 
705 static void
706 mode_edit(int fd, int page, int edit, int argc, char *argv[])
707 {
708 	int i;
709 	u_char data[255];
710 	u_char *mode_pars;
711 	struct mode_header
712 	{
713 		u_char mdl;	/* Mode data length */
714 		u_char medium_type;
715 		u_char dev_spec_par;
716 		u_char bdl;	/* Block descriptor length */
717 	};
718 
719 	struct mode_page_header
720 	{
721 		u_char page_code;
722 		u_char page_length;
723 	};
724 
725 	struct mode_header *mh;
726 	struct mode_page_header *mph;
727 
728 	char *fmt = mode_lookup(page);
729 	if (!fmt && verbose) {
730 		fprintf(stderr,
731 		"No mode data base entry in \"%s\" for page %d;  binary %s only.\n",
732 		mode_db, page, (edit ? "edit" : "display"));
733 	}
734 
735 	if (edit) {
736 		if (!fmt) {
737 			fprintf(stderr, "Sorry: can't edit without a format.\n");
738 			exit(1);
739 		}
740 
741 		if (pagectl != 0 && pagectl != 3) {
742 			fprintf(stderr,
743 "It only makes sense to edit page 0 (current) or page 3 (saved values)\n");
744 			exit(1);
745 		}
746 
747 		verbose = 1;
748 
749 		mode_sense(fd, data, sizeof(data), 1, page);
750 
751 		mh = (struct mode_header *)data;
752 		mph = (struct mode_page_header *)
753 		(((char *)mh) + sizeof(*mh) + mh->bdl);
754 
755 		mode_pars = (char *)mph + sizeof(*mph);
756 
757 		edit_init();
758 		scsireq_buff_decode_visit(mode_pars, mh->mdl,
759 		fmt, edit_check, 0);
760 
761 		mode_sense(fd, data, sizeof(data), 0, page);
762 
763 		edit_rewind();
764 		scsireq_buff_decode_visit(mode_pars, mh->mdl,
765 		fmt, edit_defaults, 0);
766 
767 		edit_rewind();
768 		scsireq_buff_decode_visit(mode_pars, mh->mdl,
769 		fmt, edit_report, 0);
770 
771 		fclose(edit_file);
772 		if (editit(edit_name) == -1 && errno != ECHILD)
773 			err(1, "edit %s", edit_name);
774 		if ((edit_file = fopen(edit_name, "r")) == NULL)
775 			err(1, "open %s", edit_name);
776 
777 		edit_rewind();
778 		scsireq_buff_encode_visit(mode_pars, mh->mdl,
779 		fmt, edit_get, 0);
780 
781 		/* Eliminate block descriptors:
782 		 */
783 		bcopy((char *)mph, ((char *)mh) + sizeof(*mh),
784 		sizeof(*mph) + mph->page_length);
785 
786 		mh->bdl = 0;
787 		mph = (struct mode_page_header *) (((char *)mh) + sizeof(*mh));
788 		mode_pars = ((char *)mph) + 2;
789 
790 #if 0
791 		/* Turn this on to see what you're sending to the
792 		 * device:
793 		 */
794 		edit_rewind();
795 		scsireq_buff_decode_visit(mode_pars,
796 		mh->mdl, fmt, arg_put, 0);
797 #endif
798 
799 		edit_done();
800 
801 		/* Make it permanent if pageselect is three.
802 		 */
803 
804 		mph->page_code &= ~0xC0;	/* Clear PS and RESERVED */
805 		mh->mdl = 0;				/* Reserved for mode select */
806 
807 		mode_select(fd, (char *)mh,
808 		sizeof(*mh) + mh->bdl + sizeof(*mph) + mph->page_length,
809 		(pagectl == 3));
810 
811 		exit(0);
812 	}
813 
814 	mode_sense(fd, data, sizeof(data), pagectl, page);
815 
816 	/* Skip over the block descriptors.
817 	 */
818 	mh = (struct mode_header *)data;
819 	mph = (struct mode_page_header *)(((char *)mh) + sizeof(*mh) + mh->bdl);
820 	mode_pars = (char *)mph + sizeof(*mph);
821 
822 	if (!fmt) {
823 		for (i = 0; i < mh->mdl; i++) {
824 			printf("%02x%c",mode_pars[i],
825 			(((i + 1) % 8) == 0) ? '\n' : ' ');
826 		}
827 		putc('\n', stdout);
828 	} else {
829 			verbose = 1;
830 			scsireq_buff_decode_visit(mode_pars,
831 			mh->mdl, fmt, arg_put, 0);
832 	}
833 }
834 
835 int
836 main(int argc, char **argv)
837 {
838 	procargs(&argc,&argv);
839 
840 	/* XXX This has grown to the point that it should be cleaned up.
841 	 */
842 	if (debugflag) {
843 		if (ioctl(fd,SCIOCDEBUG,&debuglevel) == -1)
844 			err(errno, "SCIODEBUG");
845 	} else if (commandflag) {
846 		char *fmt;
847 
848 		if (argc < 1) {
849 			fprintf(stderr, "Need the command format string.\n");
850 			usage();
851 		}
852 
853 
854 		fmt = argv[0];
855 
856 		argc -= 1;
857 		argv += 1;
858 
859 		do_cmd(fd, fmt, argc, argv);
860 	} else if (modeflag)
861 		mode_edit(fd, modepage, editflag, argc, argv);
862 
863 	exit(0);
864 }
865