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