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