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