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