xref: /freebsd/sbin/camcontrol/util.c (revision a8445737e740901f5f2c8d24c12ef7fc8b00134e)
1 /*
2  * Written By Julian ELischer
3  * Copyright julian Elischer 1993.
4  * Permission is granted to use or redistribute this file in any way as long
5  * as this notice remains. Julian Elischer does not guarantee that this file
6  * is totally correct for any given task and users of this file must
7  * accept responsibility for any damage that occurs from the application of this
8  * file.
9  *
10  * (julian@tfs.com julian@dialix.oz.au)
11  *
12  * User SCSI hooks added by Peter Dufault:
13  *
14  * Copyright (c) 1994 HD Associates
15  * (contact: dufault@hda.com)
16  * All rights reserved.
17  *
18  * Redistribution and use in source and binary forms, with or without
19  * modification, are permitted provided that the following conditions
20  * are met:
21  * 1. Redistributions of source code must retain the above copyright
22  *    notice, this list of conditions and the following disclaimer.
23  * 2. Redistributions in binary form must reproduce the above copyright
24  *    notice, this list of conditions and the following disclaimer in the
25  *    documentation and/or other materials provided with the distribution.
26  * 3. The name of HD Associates
27  *    may not be used to endorse or promote products derived from this software
28  *    without specific prior written permission.
29  *
30  * THIS SOFTWARE IS PROVIDED BY HD ASSOCIATES ``AS IS'' AND
31  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33  * ARE DISCLAIMED.  IN NO EVENT SHALL HD ASSOCIATES BE LIABLE
34  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40  * SUCH DAMAGE.
41  */
42 /*
43  * Taken from the original scsi(8) program.
44  * from: scsi.c,v 1.17 1998/01/12 07:57:57 charnier Exp $";
45  */
46 #ifndef lint
47 static const char rcsid[] =
48 	"$Id$";
49 #endif /* not lint */
50 
51 #include <ctype.h>
52 #include <err.h>
53 #include <errno.h>
54 #include <string.h>
55 #include <stdlib.h>
56 #include <stdio.h>
57 #include <sys/file.h>
58 #include <signal.h>
59 #include <unistd.h>
60 
61 #include <cam/cam.h>
62 #include <cam/cam_ccb.h>
63 #include <camlib.h>
64 #include "camcontrol.h"
65 
66 int verbose = 0;
67 
68 /* iget: Integer argument callback
69  */
70 int
71 iget(void *hook, char *name)
72 {
73 	struct get_hook *h = (struct get_hook *)hook;
74 	int arg;
75 
76 	if (h->got >= h->argc)
77 	{
78 		fprintf(stderr, "Expecting an integer argument.\n");
79 		usage();
80 		exit(1);
81 	}
82 	arg = strtol(h->argv[h->got], 0, 0);
83 	h->got++;
84 
85 	if (verbose && name && *name)
86 		printf("%s: %d\n", name, arg);
87 
88 	return arg;
89 }
90 
91 /* cget: char * argument callback
92  */
93 char *
94 cget(void *hook, char *name)
95 {
96 	struct get_hook *h = (struct get_hook *)hook;
97 	char *arg;
98 
99 	if (h->got >= h->argc)
100 	{
101 		fprintf(stderr, "Expecting a character pointer argument.\n");
102 		usage();
103 		exit(1);
104 	}
105 	arg = h->argv[h->got];
106 	h->got++;
107 
108 	if (verbose && name)
109 		printf("cget: %s: %s", name, arg);
110 
111 	return arg;
112 }
113 
114 /* arg_put: "put argument" callback
115  */
116 void
117 arg_put(void *hook, int letter, void *arg, int count, char *name)
118 {
119 	if (verbose && name && *name)
120 		printf("%s:  ", name);
121 
122 	switch(letter)
123 	{
124 		case 'i':
125 		case 'b':
126 		printf("%d ", (int)arg);
127 		break;
128 
129 		case 'c':
130 		case 'z':
131 		{
132 			char *p;
133 
134 			p = malloc(count + 1);
135 
136 			bzero(p, count +1);
137 			strncpy(p, (char *)arg, count);
138 			if (letter == 'z')
139 			{
140 				int i;
141 				for (i = count - 1; i >= 0; i--)
142 					if (p[i] == ' ')
143 						p[i] = 0;
144 					else
145 						break;
146 			}
147 			printf("%s ", p);
148 
149 			free(p);
150 		}
151 
152 		break;
153 
154 		default:
155 		printf("Unknown format letter: '%c'\n", letter);
156 	}
157 	if (verbose)
158 		putchar('\n');
159 }
160 
161 #define START_ENTRY '{'
162 #define END_ENTRY '}'
163 
164 static void
165 skipwhite(FILE *f)
166 {
167 	int c;
168 
169 skip_again:
170 
171 	while (isspace(c = getc(f)))
172 		;
173 
174 	if (c == '#') {
175 		while ((c = getc(f)) != '\n' && c != EOF)
176 			;
177 		goto skip_again;
178 	}
179 
180 	ungetc(c, f);
181 }
182 
183 /* mode_lookup: Lookup a format description for a given page.
184  */
185 char *mode_db = "/usr/share/misc/scsi_modes";
186 static char *
187 mode_lookup(int page)
188 {
189 	char *new_db;
190 	FILE *modes;
191 	int match, next, found, c;
192 	static char fmt[4096];	/* XXX This should be with strealloc */
193 	int page_desc;
194 	new_db = getenv("SCSI_MODES");
195 
196 	if (new_db)
197 		mode_db = new_db;
198 
199 	modes = fopen(mode_db, "r");
200 	if (modes == 0)
201 		return 0;
202 
203 	next = 0;
204 	found = 0;
205 
206 	while (!found) {
207 
208 		skipwhite(modes);
209 
210 		if (fscanf(modes, "%i", &page_desc) != 1)
211 			break;
212 
213 		if (page_desc == page)
214 			found = 1;
215 
216 		skipwhite(modes);
217 		if (getc(modes) != START_ENTRY)
218 			errx(1, "expected %c", START_ENTRY);
219 
220 		match = 1;
221 		while (match != 0) {
222 			c = getc(modes);
223 			if (c == EOF) {
224 				warnx("expected %c", END_ENTRY);
225 			}
226 
227 			if (c == START_ENTRY) {
228 				match++;
229 			}
230 			if (c == END_ENTRY) {
231 				match--;
232 				if (match == 0)
233 					break;
234 			}
235 			if (found && c != '\n') {
236 				if (next >= sizeof(fmt))
237 					errx(1, "buffer overflow");
238 
239 				fmt[next++] = (u_char)c;
240 			}
241 		}
242 	}
243 	fmt[next] = 0;
244 
245 	return (found) ? fmt : 0;
246 }
247 
248 /* -------- edit: Mode Select Editor ---------
249  */
250 struct editinfo
251 {
252 	int can_edit;
253 	int default_value;
254 } editinfo[64];	/* XXX Bogus fixed size */
255 
256 static int editind;
257 volatile int edit_opened;
258 static FILE *edit_file;
259 static char edit_name[L_tmpnam];
260 
261 static inline void
262 edit_rewind(void)
263 {
264 	editind = 0;
265 }
266 
267 static void
268 edit_done(void)
269 {
270 	int opened;
271 
272 	sigset_t all, prev;
273 	sigfillset(&all);
274 
275 	(void)sigprocmask(SIG_SETMASK, &all, &prev);
276 
277 	opened = (int)edit_opened;
278 	edit_opened = 0;
279 
280 	(void)sigprocmask(SIG_SETMASK, &prev, 0);
281 
282 	if (opened)
283 	{
284 		if (fclose(edit_file))
285 			warn("%s", edit_name);
286 		if (unlink(edit_name))
287 			warn("%s", edit_name);
288 	}
289 }
290 
291 static void
292 edit_init(void)
293 {
294 	edit_rewind();
295 	if (tmpnam(edit_name) == 0)
296 		errx(1, "tmpnam failed");
297 	if ((edit_file = fopen(edit_name, "w")) == 0)
298 		err(1, "%s", edit_name);
299 	edit_opened = 1;
300 
301 	atexit(edit_done);
302 }
303 
304 static void
305 edit_check(void *hook, int letter, void *arg, int count, char *name)
306 {
307 	if (letter != 'i' && letter != 'b')
308 		errx(1, "can't edit format %c", letter);
309 
310 	if (editind >= sizeof(editinfo) / sizeof(editinfo[0]))
311 		errx(1, "edit table overflow");
312 
313 	editinfo[editind].can_edit = ((int)arg != 0);
314 	editind++;
315 }
316 
317 static void
318 edit_defaults(void *hook, int letter, void *arg, int count, char *name)
319 {
320 	if (letter != 'i' && letter != 'b')
321 		errx(1, "can't edit format %c", letter);
322 
323 	editinfo[editind].default_value = ((int)arg);
324 	editind++;
325 }
326 
327 static void
328 edit_report(void *hook, int letter, void *arg, int count, char *name)
329 {
330 	if (editinfo[editind].can_edit) {
331 		if (letter != 'i' && letter != 'b')
332 			errx(1, "can't report format %c", letter);
333 
334 		fprintf(edit_file, "%s:  %d\n", name, (int)arg);
335 	}
336 
337 	editind++;
338 }
339 
340 static int
341 edit_get(void *hook, char *name)
342 {
343 	int arg = editinfo[editind].default_value;
344 
345 	if (editinfo[editind].can_edit) {
346 		char line[80];
347 		if (fgets(line, sizeof(line), edit_file) == 0)
348 			err(1, "fgets");
349 
350 		line[strlen(line) - 1] = 0;
351 
352 		if (strncmp(name, line, strlen(name)) != 0)
353 			errx(1, "expected \"%s\" and read \"%s\"", name, line);
354 
355 		arg = strtoul(line + strlen(name) + 2, 0, 0);
356 	}
357 
358 	editind++;
359 	return arg;
360 }
361 
362 static void
363 edit_edit(void)
364 {
365 	char *system_line;
366 	char *editor = getenv("EDITOR");
367 	if (!editor)
368 		editor = "vi";
369 
370 	fclose(edit_file);
371 
372 	system_line = malloc(strlen(editor) + strlen(edit_name) + 6);
373 	sprintf(system_line, "%s %s", editor, edit_name);
374 	system(system_line);
375 	free(system_line);
376 
377 	if ((edit_file = fopen(edit_name, "r")) == 0)
378 		err(1, "%s", edit_name);
379 }
380 
381 void
382 mode_edit(struct cam_device *device, int page, int page_control, int dbd,
383 	  int edit, int retry_count, int timeout)
384 {
385 	int i;
386 	u_char data[255];
387 	u_char *mode_pars;
388 	struct mode_header
389 	{
390 		u_char mdl;	/* Mode data length */
391 		u_char medium_type;
392 		u_char dev_spec_par;
393 		u_char bdl;	/* Block descriptor length */
394 	};
395 
396 	struct mode_page_header
397 	{
398 		u_char page_code;
399 		u_char page_length;
400 	};
401 
402 	struct mode_header *mh;
403 	struct mode_page_header *mph;
404 
405 	char *fmt = mode_lookup(page);
406 	if (!fmt && verbose) {
407 		fprintf(stderr,
408 		"No mode data base entry in \"%s\" for page %d; "
409 		" binary %s only.\n",
410 		mode_db, page, (edit ? "edit" : "display"));
411 	}
412 
413 	if (edit) {
414 		if (!fmt)
415 			errx(1, "can't edit without a format");
416 
417 		if (page_control != 0 && page_control != 3)
418 			errx(1, "it only makes sense to edit page 0 "
419 			     "(current) or page 3 (saved values)");
420 
421 		verbose = 1;
422 
423 		mode_sense(device, page, 1, dbd, retry_count, timeout,
424 			   data, sizeof(data));
425 
426 		mh = (struct mode_header *)data;
427 		mph = (struct mode_page_header *)
428 		(((char *)mh) + sizeof(*mh) + mh->bdl);
429 
430 		mode_pars = (char *)mph + sizeof(*mph);
431 
432 		edit_init();
433 		buff_decode_visit(mode_pars, mh->mdl, fmt, edit_check, 0);
434 
435 		mode_sense(device, page, 0, dbd, retry_count, timeout,
436 			   data, sizeof(data));
437 
438 		edit_rewind();
439 		buff_decode_visit(mode_pars, mh->mdl, fmt, edit_defaults, 0);
440 
441 		edit_rewind();
442 		buff_decode_visit(mode_pars, mh->mdl, fmt, edit_report, 0);
443 
444 		edit_edit();
445 
446 		edit_rewind();
447 		buff_encode_visit(mode_pars, mh->mdl, fmt, edit_get, 0);
448 
449 		/* Eliminate block descriptors:
450 		 */
451 		bcopy((char *)mph, ((char *)mh) + sizeof(*mh),
452 		sizeof(*mph) + mph->page_length);
453 
454 		mh->bdl = mh->dev_spec_par = 0;
455 		mph = (struct mode_page_header *) (((char *)mh) + sizeof(*mh));
456 		mode_pars = ((char *)mph) + 2;
457 
458 #if 0
459 		/* Turn this on to see what you're sending to the
460 		 * device:
461 		 */
462 		edit_rewind();
463 		buff_decode_visit(mode_pars, mh->mdl, fmt, arg_put, 0);
464 #endif
465 
466 		edit_done();
467 
468 		/* Make it permanent if pageselect is three.
469 		 */
470 
471 		mph->page_code &= ~0xC0;	/* Clear PS and RESERVED */
472 		mh->mdl = 0;			/* Reserved for mode select */
473 
474 		mode_select(device, (page_control == 3), retry_count,
475 			    timeout, (u_int8_t *)mh, sizeof(*mh) + mh->bdl +
476 			    sizeof(*mph) + mph->page_length);
477 
478 		return;
479 	}
480 
481 	mode_sense(device, page, page_control, dbd, retry_count, timeout,
482 		   data, sizeof(data));
483 
484 	/* Skip over the block descriptors.
485 	 */
486 	mh = (struct mode_header *)data;
487 	mph = (struct mode_page_header *)(((char *)mh) + sizeof(*mh) + mh->bdl);
488 	mode_pars = (char *)mph + sizeof(*mph);
489 
490 	if (!fmt) {
491 		for (i = 0; i < mh->mdl; i++) {
492 			printf("%02x%c",mode_pars[i],
493 			       (((i + 1) % 8) == 0) ? '\n' : ' ');
494 		}
495 		putc('\n', stdout);
496 	} else {
497 		verbose = 1;
498 		buff_decode_visit(mode_pars, mh->mdl, fmt, arg_put, NULL);
499 	}
500 }
501