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