xref: /openbsd-src/sbin/scsi/scsi.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$OpenBSD: scsi.c,v 1.8 2001/07/07 18:26:21 deraadt 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 <stdio.h>
47 #include <string.h>
48 #include <stdlib.h>
49 #include <unistd.h>
50 #include <errno.h>
51 #include <sys/scsiio.h>
52 #include <sys/file.h>
53 #include <scsi.h>
54 #include <ctype.h>
55 #include <signal.h>
56 
57 int	fd;
58 int	debuglevel;
59 int	debugflag;
60 int commandflag;
61 int	reprobe;
62 #ifdef SCIOCADDR
63 int	probe_all;
64 #endif
65 int verbose = 0;
66 int	bus = -1;	/* all busses */
67 int	targ = -1;	/* all targs */
68 int	lun = 0;	/* just lun 0 */
69 #ifdef SCIOCFREEZE
70 int	freeze = 0;	/* Freeze this many seconds */
71 #endif
72 
73 int modeflag;
74 int editflag;
75 int modepage = 0; /* Read this mode page */
76 int pagectl = 0;  /* Mode sense page control */
77 int seconds = 2;
78 
79 void usage(void)
80 {
81 	printf(
82 
83 "Usage:\n"
84 "\n"
85 "  scsi -f device -d debug_level                    # To set debug level\n"
86 #ifdef SCIOCFREEZE
87 "  scsi -f device [-v] -z seconds                   # To freeze bus\n"
88 #endif
89 "  scsi -f device -m page [-P pc]                   # To read mode pages\n"
90 "  scsi -f device -p [-b bus] [-l lun]              # To probe all devices\n"
91 "  scsi -f device -r [-b bus] [-t targ] [-l lun]    # To reprobe a device\n"
92 "  scsi -f device [-v] [-s seconds] -c cmd_fmt [arg0 ... argn] # A command...\n"
93 "                 -o count out_fmt [arg0 ... argn]  #   EITHER (data out)\n"
94 "                 -i count in_fmt                   #   OR     (data in)\n"
95 "\n"
96 "\"out_fmt\" can be \"-\" to read output data from stdin;\n"
97 "\"in_fmt\" can be \"-\" to write input data to stdout;\n"
98 "\n"
99 "If debugging is not compiled in the kernel, \"-d\" will have no effect\n"
100 
101 );
102 
103 	exit (1);
104 }
105 
106 void procargs(int *argc_p, char ***argv_p)
107 {
108 	int argc = *argc_p;
109 	char **argv = *argv_p;
110 	int		    fflag,
111 	                    ch;
112 
113 	fflag = 0;
114 	commandflag = 0;
115 	debugflag = 0;
116 	while ((ch = getopt(argc, argv, "ceprvf:d:b:t:l:z:m:P:s:")) != -1) {
117 		switch (ch) {
118 #ifdef SCIOCADDR
119 		case 'p':
120 			probe_all = 1;
121 			break;
122 #endif
123 		case 'r':
124 			reprobe = 1;
125 			break;
126 		case 'c':
127 			commandflag = 1;
128 			break;
129 		case 'v':
130 			verbose = 1;
131 			break;
132 		case 'e':
133 			editflag = 1;
134 			break;
135 		case 'f':
136 			if ((fd = scsi_open(optarg, O_RDWR)) < 0) {
137 				(void) fprintf(stderr,
138 					  "%s: unable to open device %s: %s\n",
139 					       argv[0], optarg, strerror(errno));
140 				exit(errno);
141 			}
142 			fflag = 1;
143 			break;
144 		case 'd':
145 			debuglevel = strtol(optarg, 0, 0);
146 			debugflag = 1;
147 			break;
148 		case 'b':
149 			bus = strtol(optarg, 0, 0);
150 			break;
151 		case 't':
152 			targ = strtol(optarg, 0, 0);
153 			break;
154 		case 'l':
155 			lun = strtol(optarg, 0, 0);
156 			break;
157 #ifdef SCIOCFREEZE
158 		case 'z':
159 			freeze = strtol(optarg, 0, 0);
160 			break;
161 #endif
162 		case 'P':
163 			pagectl = strtol(optarg, 0, 0);
164 			break;
165 		case 's':
166 			seconds = strtol(optarg, 0, 0);
167 			break;
168 		case 'm':
169 			modeflag = 1;
170 			modepage = strtol(optarg, 0, 0);
171 			break;
172 		case '?':
173 		default:
174 			usage();
175 		}
176 	}
177 	*argc_p = argc - optind;
178 	*argv_p = argv + optind;
179 
180 	if (!fflag) usage();
181 }
182 
183 /* get_hook: Structure for evaluating args in a callback.
184  */
185 struct get_hook
186 {
187 	int argc;
188 	char **argv;
189 	int got;
190 };
191 
192 /* iget: Integer argument callback
193  */
194 int iget(void *hook, char *name)
195 {
196 	struct get_hook *h = (struct get_hook *)hook;
197 	int arg;
198 
199 	if (h->got >= h->argc)
200 	{
201 		fprintf(stderr, "Expecting an integer argument.\n");
202 		usage();
203 	}
204 	arg = strtol(h->argv[h->got], 0, 0);
205 	h->got++;
206 
207 	if (verbose && name && *name)
208 		printf("%s: %d\n", name, arg);
209 
210 	return arg;
211 }
212 
213 /* cget: char * argument callback
214  */
215 char *cget(void *hook, char *name)
216 {
217 	struct get_hook *h = (struct get_hook *)hook;
218 	char *arg;
219 
220 	if (h->got >= h->argc)
221 	{
222 		fprintf(stderr, "Expecting a character pointer argument.\n");
223 		usage();
224 	}
225 	arg = h->argv[h->got];
226 	h->got++;
227 
228 	if (verbose && name)
229 		printf("cget: %s: %s", name, arg);
230 
231 	return arg;
232 }
233 
234 /* arg_put: "put argument" callback
235  */
236 void arg_put(void *hook, int letter, void *arg, int count, char *name)
237 {
238 	if (verbose && name && *name)
239 		printf("%s:  ", name);
240 
241 	switch(letter)
242 	{
243 		case 'i':
244 		case 'b':
245 		printf("%ld ", (long)arg);
246 		break;
247 
248 		case 'c':
249 		case 'z':
250 		{
251 			char *p = malloc(count + 1);
252 			p[count] = 0;
253 			strncpy(p, (char *)arg, count);
254 			if (letter == 'z')
255 			{
256 				int i;
257 				for (i = count - 1; i >= 0; i--)
258 					if (p[i] == ' ')
259 						p[i] = 0;
260 					else
261 						break;
262 			}
263 			printf("%s ", p);
264 		}
265 
266 		break;
267 
268 		default:
269 		printf("Unknown format letter: '%c'\n", letter);
270 	}
271 	if (verbose)
272 		putchar('\n');
273 }
274 
275 int arg_get (void *hook, char *field_name)
276 {
277 	printf("get \"%s\".\n", field_name);
278 	return 0;
279 }
280 
281 /* data_phase: SCSI bus data phase: DATA IN, DATA OUT, or no data transfer.
282  */
283 enum data_phase {none = 0, in, out};
284 
285 /* do_cmd: Send a command to a SCSI device
286  */
287 static void
288 do_cmd(int fd, char *fmt, int argc, char **argv)
289 {
290 	struct get_hook h;
291 	scsireq_t *scsireq = scsireq_new();
292 	enum data_phase data_phase;
293 	int count, amount;
294 	char *data_fmt, *bp;
295 
296 	h.argc = argc;
297 	h.argv = argv;
298 	h.got = 0;
299 
300 	scsireq_reset(scsireq);
301 
302 	scsireq_build_visit(scsireq, 0, 0, 0, fmt, iget, (void *)&h);
303 
304 	/* Three choices here:
305 	 * 1. We've used up all the args and have no data phase.
306 	 * 2. We have input data ("-i")
307 	 * 3. We have output data ("-o")
308 	 */
309 
310 	if (h.got >= h.argc)
311 	{
312 		data_phase = none;
313 		count = scsireq->datalen = 0;
314 	}
315 	else
316 	{
317 		char *flag = cget(&h, 0);
318 
319 		if (strcmp(flag, "-o") == 0)
320 		{
321 			data_phase = out;
322 			scsireq->flags = SCCMD_WRITE;
323 		}
324 		else if (strcmp(flag, "-i") == 0)
325 		{
326 			data_phase = in;
327 			scsireq->flags = SCCMD_READ;
328 		}
329 		else
330 		{
331 			fprintf(stderr,
332 			"Need either \"-i\" or \"-o\" for data phase; not \"%s\".\n", flag);
333 			usage();
334 		}
335 
336 		count = scsireq->datalen = iget(&h, 0);
337 		if (count)
338 		{
339 			data_fmt = cget(&h, 0);
340 
341 			scsireq->databuf = malloc(count);
342 
343 			if (data_phase == out)
344 			{
345 				if (strcmp(data_fmt, "-") == 0)	/* Read data from stdin */
346 				{
347 					bp = (char *)scsireq->databuf;
348 					while (count > 0 && (amount = read(0, bp, count)) > 0)
349 					{
350 						count -= amount;
351 						bp += amount;
352 					}
353 					if (amount == -1)
354 					{
355 						perror("read");
356 						exit(errno);
357 					}
358 					else if (amount == 0)
359 					{
360 						/* early EOF */
361 						fprintf(stderr,
362 							"Warning: only read %lu bytes out of %lu.\n",
363 							scsireq->datalen - (u_long)count,
364 							scsireq->datalen);
365 						scsireq->datalen -= (u_long)count;
366 					}
367 				}
368 				else
369 				{
370 					bzero(scsireq->databuf, count);
371 					scsireq_encode_visit(scsireq, data_fmt, iget, (void *)&h);
372 				}
373 			}
374 		}
375 	}
376 
377 
378 	scsireq->timeout = seconds * 1000;
379 
380 	if (scsireq_enter(fd, scsireq) == -1)
381 	{
382 		scsi_debug(stderr, -1, scsireq);
383 		exit(errno);
384 	}
385 
386 	if (SCSIREQ_ERROR(scsireq))
387 		scsi_debug(stderr, 0, scsireq);
388 
389 	if (count && data_phase == in)
390 	{
391 		if (strcmp(data_fmt, "-") == 0)	/* stdout */
392 		{
393 			bp = (char *)scsireq->databuf;
394 			while (count > 0 && (amount = write(1, bp, count)) > 0)
395 			{
396 				count -= amount;
397 				bp += amount;
398 			}
399 			if (amount < 0)
400 			{
401 				perror("write");
402 				exit(errno);
403 			}
404 			else if (amount == 0)
405 				fprintf(stderr, "Warning: wrote only %u bytes out of %u.\n",
406 					scsireq->datalen - count,
407 					scsireq->datalen);
408 
409 		}
410 		else
411 		{
412 			scsireq_decode_visit(scsireq, data_fmt, arg_put, 0);
413 			putchar('\n');
414 		}
415 	}
416 }
417 
418 #ifdef SCIOCFREEZE
419 static void
420 freeze_ioctl(int fd, int op, void *data)
421 {
422 	if (ioctl(fd, SCIOCFREEZE, 0) == -1) {
423 		if (errno == ENODEV) {
424 			fprintf(stderr,
425 			"Your kernel must be configured with option SCSI_FREEZE.\n");
426 		}
427 		else
428 			perror("SCIOCFREEZE");
429 		exit(errno);
430 	}
431 }
432 
433 /* do_freeze: Freeze the bus for a given number of seconds.
434  */
435 static void do_freeze(int seconds)
436 {
437 	if (seconds == -1) {
438 		printf("Hit return to thaw:  ");
439 		fflush(stdout);
440 		sync();
441 
442 		freeze_ioctl(fd, SCIOCFREEZE, 0);
443 
444 		(void)getchar();
445 
446 		freeze_ioctl(fd, SCIOCTHAW, 0);
447 	}
448 	else {
449 		sync();
450 		freeze_ioctl(fd, SCIOCFREEZETHAW, &seconds);
451 		if (verbose) {
452 			putchar('\007');
453 			fflush(stdout);
454 		}
455 
456 		freeze_ioctl(fd, SCIOCWAITTHAW, 0);
457 		if (verbose) {
458 			putchar('\007');
459 			fflush(stdout);
460 		}
461 	}
462 }
463 #endif
464 
465 void mode_sense(int fd, u_char *data, int len, int pc, int page)
466 {
467 	scsireq_t *scsireq;
468 
469 	bzero(data, len);
470 
471 	scsireq = scsireq_new();
472 
473 	if (scsireq_enter(fd, scsireq_build(scsireq,
474 	 len, data, SCCMD_READ,
475 	 "1A 0 v:2 {Page Control} v:6 {Page Code} 0 v:i1 {Allocation Length} 0",
476 	 pc, page, len)) == -1)	/* Mode sense */
477 	{
478 		scsi_debug(stderr, -1, scsireq);
479 		exit(errno);
480 	}
481 
482 	if (SCSIREQ_ERROR(scsireq))
483 	{
484 		scsi_debug(stderr, 0, scsireq);
485 		exit(1);
486 	}
487 
488 	free(scsireq);
489 }
490 
491 void mode_select(int fd, u_char *data, int len, int perm)
492 {
493 	scsireq_t *scsireq;
494 
495 	scsireq = scsireq_new();
496 
497 	if (scsireq_enter(fd, scsireq_build(scsireq,
498 	 len, data, SCCMD_WRITE,
499 	 "15 0:7 v:1 {SP} 0 0 v:i1 {Allocation Length} 0", perm, len)) == -1)	/* Mode select */
500 	{
501 		scsi_debug(stderr, -1, scsireq);
502 		exit(errno);
503 	}
504 
505 	if (SCSIREQ_ERROR(scsireq))
506 	{
507 		scsi_debug(stderr, 0, scsireq);
508 		exit(1);
509 	}
510 
511 	free(scsireq);
512 }
513 
514 
515 #define START_ENTRY '{'
516 #define END_ENTRY '}'
517 
518 static void
519 skipwhite(FILE *f)
520 {
521 	int c;
522 
523 skip_again:
524 
525 	while (isspace(c = getc(f)))
526 		;
527 
528 	if (c == '#') {
529 		while ((c = getc(f)) != '\n' && c != EOF)
530 			;
531 		goto skip_again;
532 	}
533 
534 	ungetc(c, f);
535 }
536 
537 /* mode_lookup: Lookup a format description for a given page.
538  */
539 char *mode_db = "/usr/share/misc/scsi_modes";
540 static char *mode_lookup(int page)
541 {
542 	char *new_db;
543 	FILE *modes;
544 	int match, next, found, c;
545 	static char fmt[1024];	/* XXX This should be with strealloc */
546 	int page_desc;
547 	new_db = getenv("SCSI_MODES");
548 
549 	if (new_db)
550 		mode_db = new_db;
551 
552 	modes = fopen(mode_db, "r");
553 	if (modes == 0)
554 		return 0;
555 
556 	next = 0;
557 	found = 0;
558 
559 	while (!found) {
560 
561 		skipwhite(modes);
562 
563 		if (fscanf(modes, "%i", &page_desc) != 1)
564 			break;
565 
566 		if (page_desc == page)
567 			found = 1;
568 
569 		skipwhite(modes);
570 		if (getc(modes) != START_ENTRY) {
571 			fprintf(stderr, "Expected %c.\n", START_ENTRY);
572 			exit(1);
573 		}
574 
575 		match = 1;
576 		while (match != 0) {
577 			c = getc(modes);
578 			if (c == EOF) {
579 				fprintf(stderr, "Expected %c.\n", END_ENTRY);
580 			}
581 
582 			if (c == START_ENTRY) {
583 				match++;
584 			}
585 			if (c == END_ENTRY) {
586 				match--;
587 				if (match == 0)
588 					break;
589 			}
590 			if (found && c != '\n') {
591 				if (next >= sizeof(fmt)) {
592 					fprintf(stderr, "Stupid program: Buffer overflow.\n");
593 					exit(ENOMEM);
594 				}
595 
596 				fmt[next++] = (u_char)c;
597 			}
598 		}
599 	}
600 	fmt[next] = 0;
601 
602 	return (found) ? fmt : 0;
603 }
604 
605 /* -------- edit: Mode Select Editor ---------
606  */
607 struct editinfo
608 {
609 	long can_edit;
610 	long default_value;
611 } editinfo[64];	/* XXX Bogus fixed size */
612 
613 static int editind;
614 volatile int edit_opened;
615 static FILE *edit_file;
616 static char edit_name[L_tmpnam];
617 
618 static inline void
619 edit_rewind(void)
620 {
621 	editind = 0;
622 }
623 
624 static void
625 edit_done(void)
626 {
627 	int opened;
628 
629 	sigset_t all, prev;
630 	sigfillset(&all);
631 
632 	(void)sigprocmask(SIG_SETMASK, &all, &prev);
633 
634 	opened = (int)edit_opened;
635 	edit_opened = 0;
636 
637 	(void)sigprocmask(SIG_SETMASK, &prev, 0);
638 
639 	if (opened)
640 	{
641 		if (fclose(edit_file))
642 			perror(edit_name);
643 		if (unlink(edit_name))
644 			perror(edit_name);
645 	}
646 }
647 
648 static void
649 edit_init(void)
650 {
651 	int fd;
652 
653 	edit_rewind();
654 	strcpy(edit_name, "/var/tmp/scXXXXXXXX");
655 	if ((fd = mkstemp(edit_name)) == -1) {
656 		perror("mkstemp failed");
657 		exit(errno);
658 	}
659 	if ( (edit_file = fdopen(fd, "w+")) == 0) {
660 		perror("fdopen failed");
661 		exit(errno);
662 	}
663 	edit_opened = 1;
664 
665 	atexit(edit_done);
666 }
667 
668 static void
669 edit_check(void *hook, int letter, void *arg, int count, char *name)
670 {
671 	if (letter != 'i' && letter != 'b') {
672 		fprintf(stderr, "Can't edit format %c.\n", letter);
673 		exit(1);
674 	}
675 
676 	if (editind >= sizeof(editinfo) / sizeof(editinfo[0])) {
677 		fprintf(stderr, "edit table overflow\n");
678 		exit(ENOMEM);
679 	}
680 	editinfo[editind].can_edit = ((long)arg != 0);
681 	editind++;
682 }
683 
684 static void
685 edit_defaults(void *hook, int letter, void *arg, int count, char *name)
686 {
687 	if (letter != 'i' && letter != 'b') {
688 		fprintf(stderr, "Can't edit format %c.\n", letter);
689 		exit(1);
690 	}
691 
692 	editinfo[editind].default_value = ((long)arg);
693 	editind++;
694 }
695 
696 static void
697 edit_report(void *hook, int letter, void *arg, int count, char *name)
698 {
699 	if (editinfo[editind].can_edit) {
700 		if (letter != 'i' && letter != 'b') {
701 			fprintf(stderr, "Can't report format %c.\n", letter);
702 			exit(1);
703 		}
704 
705 		fprintf(edit_file, "%s:  %ld\n", name, (long)arg);
706 	}
707 
708 	editind++;
709 }
710 
711 static int
712 edit_get(void *hook, char *name)
713 {
714 	int arg = editinfo[editind].default_value;
715 
716 	if (editinfo[editind].can_edit) {
717 		char line[80];
718 		if (fgets(line, sizeof(line), edit_file) == 0) {
719 			perror("fgets");
720 			exit(errno);
721 		}
722 
723 		line[strlen(line) - 1] = 0;
724 
725 		if (strncmp(name, line, strlen(name)) != 0) {
726 			fprintf(stderr, "Expected \"%s\" and read \"%s\"\n",
727 			name, line);
728 			exit(1);
729 		}
730 
731 		arg = strtoul(line + strlen(name) + 2, 0, 0);
732 	}
733 
734 	editind++;
735 	return arg;
736 }
737 
738 static void
739 edit_edit(void)
740 {
741 	char *system_line;
742 	char *editor = getenv("EDITOR");
743 	if (!editor)
744 		editor = "vi";
745 
746 	fclose(edit_file);
747 
748 	system_line = malloc(strlen(editor) + strlen(edit_name) + 6);
749 	sprintf(system_line, "%s %s", editor, edit_name);
750 	system(system_line);
751 	free(system_line);
752 
753 	if ( (edit_file = fopen(edit_name, "r")) == 0) {
754 		perror(edit_name);
755 		exit(errno);
756 	}
757 }
758 
759 static void
760 mode_edit(int fd, int page, int edit, int argc, char *argv[])
761 {
762 	int i;
763 	u_char data[255];
764 	u_char *mode_pars;
765 	struct mode_header
766 	{
767 		u_char mdl;	/* Mode data length */
768 		u_char medium_type;
769 		u_char dev_spec_par;
770 		u_char bdl;	/* Block descriptor length */
771 	};
772 
773 	struct mode_page_header
774 	{
775 		u_char page_code;
776 		u_char page_length;
777 	};
778 
779 	struct mode_header *mh;
780 	struct mode_page_header *mph;
781 
782 	char *fmt = mode_lookup(page);
783 	if (!fmt && verbose) {
784 		fprintf(stderr,
785 		"No mode data base entry in \"%s\" for page %d;  binary %s only.\n",
786 		mode_db, page, (edit ? "edit" : "display"));
787 	}
788 
789 	if (edit) {
790 		if (!fmt) {
791 			fprintf(stderr, "Sorry: can't edit without a format.\n");
792 			exit(1);
793 		}
794 
795 		if (pagectl != 0 && pagectl != 3) {
796 			fprintf(stderr,
797 "It only makes sense to edit page 0 (current) or page 3 (saved values)\n");
798 			exit(1);
799 		}
800 
801 		verbose = 1;
802 
803 		mode_sense(fd, data, sizeof(data), 1, page);
804 
805 		mh = (struct mode_header *)data;
806 		mph = (struct mode_page_header *)
807 		(((char *)mh) + sizeof(*mh) + mh->bdl);
808 
809 		mode_pars = (char *)mph + sizeof(*mph);
810 
811 		edit_init();
812 		scsireq_buff_decode_visit(mode_pars, mh->mdl,
813 		fmt, edit_check, 0);
814 
815 		mode_sense(fd, data, sizeof(data), 0, page);
816 
817 		edit_rewind();
818 		scsireq_buff_decode_visit(mode_pars, mh->mdl,
819 		fmt, edit_defaults, 0);
820 
821 		edit_rewind();
822 		scsireq_buff_decode_visit(mode_pars, mh->mdl,
823 		fmt, edit_report, 0);
824 
825 		edit_edit();
826 
827 		edit_rewind();
828 		scsireq_buff_encode_visit(mode_pars, mh->mdl,
829 		fmt, edit_get, 0);
830 
831 		/* Eliminate block descriptors:
832 		 */
833 		bcopy((char *)mph, ((char *)mh) + sizeof(*mh),
834 		sizeof(*mph) + mph->page_length);
835 
836 		mh->bdl = 0;
837 		mph = (struct mode_page_header *) (((char *)mh) + sizeof(*mh));
838 		mode_pars = ((char *)mph) + 2;
839 
840 #if 0
841 		/* Turn this on to see what you're sending to the
842 		 * device:
843 		 */
844 		edit_rewind();
845 		scsireq_buff_decode_visit(mode_pars,
846 		mh->mdl, fmt, arg_put, 0);
847 #endif
848 
849 		edit_done();
850 
851 		/* Make it permanent if pageselect is three.
852 		 */
853 
854 		mph->page_code &= ~0xC0;	/* Clear PS and RESERVED */
855 		mh->mdl = 0;				/* Reserved for mode select */
856 
857 		mode_select(fd, (char *)mh,
858 		sizeof(*mh) + mh->bdl + sizeof(*mph) + mph->page_length,
859 		(pagectl == 3));
860 
861 		exit(0);
862 	}
863 
864 	mode_sense(fd, data, sizeof(data), pagectl, page);
865 
866 	/* Skip over the block descriptors.
867 	 */
868 	mh = (struct mode_header *)data;
869 	mph = (struct mode_page_header *)(((char *)mh) + sizeof(*mh) + mh->bdl);
870 	mode_pars = (char *)mph + sizeof(*mph);
871 
872 	if (!fmt) {
873 		for (i = 0; i < mh->mdl; i++) {
874 			printf("%02x%c",mode_pars[i],
875 			(((i + 1) % 8) == 0) ? '\n' : ' ');
876 		}
877 		putc('\n', stdout);
878 	} else {
879 			verbose = 1;
880 			scsireq_buff_decode_visit(mode_pars,
881 			mh->mdl, fmt, arg_put, 0);
882 	}
883 }
884 
885 #ifdef SCIOCADDR
886 /* do_probe_all: Loop over all SCSI IDs and see if something is
887  * there.  This only does BUS 0 LUN 0.
888  */
889 void do_probe_all(void)
890 {
891 	scsireq_t *scsireq;
892 
893 	char vendor_id[8 + 1], product_id[16 + 1], revision[4 + 1];
894 	int id;
895 	u_char *inq_buf = malloc(96);
896 	struct scsi_addr addr;
897 
898 	scsireq = scsireq_build(scsireq_new(),
899 	96, inq_buf, SCCMD_READ,
900 	"12 0 0 0 v 0", 96);
901 
902 	addr.scbus = (bus == -1) ? 0 : bus;
903 	addr.lun = lun;
904 
905 	if (addr.scbus || addr.lun)
906 	{
907 		printf("For bus %d lun %d:\n", addr.scbus, addr.lun);
908 	}
909 
910 	for (id = 0; id < 8; id++)
911 	{
912 		addr.target = id;
913 
914 		printf("%d: ", id);
915 		if (ioctl(fd, SCIOCADDR, &addr) == -1) {
916 			if (errno == ENXIO)
917 			{
918 				errno = 0;
919 				printf("nothing.\n");
920 			}
921 			else
922 				printf("SCIOCADDR: %s\n", strerror(errno));
923 
924 			continue;
925 		}
926 
927 		if (scsireq_enter(fd, scsireq) == -1) {
928 			printf("scsireq_enter: %s\n", strerror(errno));
929 			continue;
930 		}
931 
932 		vendor_id[sizeof(vendor_id) - 1] = 0;
933 		product_id[sizeof(product_id) - 1] = 0;
934 		revision[sizeof(revision) - 1] = 0;
935 
936 		scsireq_decode(scsireq, "s8 c8 c16 c4",
937 		vendor_id, product_id, revision);
938 
939 		printf("%s %s %s\n", vendor_id, product_id, revision);
940 	}
941 }
942 #endif
943 
944 int
945 main(int argc, char **argv)
946 {
947 	struct scsi_addr scaddr;
948 
949 	procargs(&argc,&argv);
950 
951 	/* XXX This has grown to the point that it should be cleaned up.
952 	 */
953 #ifdef SCIOCFREEZE
954 	if (freeze) {
955 		do_freeze(freeze);
956 	} else
957 #endif
958 #ifdef SCIOCADDR
959 	if (probe_all) {
960 		do_probe_all();
961 	} else
962 #endif
963 	if(reprobe) {
964 		scaddr.scbus = bus;
965 		scaddr.target = targ;
966 		scaddr.lun = lun;
967 
968 		if (ioctl(fd,SCIOCREPROBE,&scaddr) == -1)
969 			perror("ioctl");
970 	} else if(debugflag) {
971 		if (ioctl(fd,SCIOCDEBUG,&debuglevel) == -1)
972 		{
973 			perror("ioctl [SCIODEBUG]");
974 			exit(1);
975 		}
976 	} else if (commandflag) {
977 		char *fmt;
978 
979 		if (argc < 1) {
980 			fprintf(stderr, "Need the command format string.\n");
981 			usage();
982 		}
983 
984 
985 		fmt = argv[0];
986 
987 		argc -= 1;
988 		argv += 1;
989 
990 		do_cmd(fd, fmt, argc, argv);
991 	} else if (modeflag) {
992 		mode_edit(fd, modepage, editflag, argc, argv);
993 	}
994 	exit(0);
995 }
996