xref: /freebsd/sbin/camcontrol/modeedit.c (revision a79b71281cd63ad7a6cc43a6d5673a2510b51630)
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   "$FreeBSD$";
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(0);
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(0);
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 ", (intptr_t)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 	int fd;
295 
296 	edit_rewind();
297 	strlcpy(edit_name, "/tmp/camXXXXXX", sizeof(edit_name));
298 	if ((fd = mkstemp(edit_name)) == -1)
299 		errx(1, "mkstemp failed");
300 	if ((edit_file = fdopen(fd, "w")) == 0)
301 		err(1, "%s", edit_name);
302 	edit_opened = 1;
303 
304 	atexit(edit_done);
305 }
306 
307 static void
308 edit_check(void *hook, int letter, void *arg, int count, char *name)
309 {
310 	if (letter != 'i' && letter != 'b')
311 		errx(1, "can't edit format %c", letter);
312 
313 	if (editind >= sizeof(editinfo) / sizeof(editinfo[0]))
314 		errx(1, "edit table overflow");
315 
316 	editinfo[editind].can_edit = (arg != NULL);
317 	editind++;
318 }
319 
320 static void
321 edit_defaults(void *hook, int letter, void *arg, int count, char *name)
322 {
323 	if (letter != 'i' && letter != 'b')
324 		errx(1, "can't edit format %c", letter);
325 
326 	editinfo[editind].default_value = (intptr_t)arg;	/* truncated */
327 	editind++;
328 }
329 
330 static void
331 edit_report(void *hook, int letter, void *arg, int count, char *name)
332 {
333 	if (editinfo[editind].can_edit) {
334 		if (letter != 'i' && letter != 'b')
335 			errx(1, "can't report format %c", letter);
336 
337 		fprintf(edit_file, "%s:  %d\n", name, (intptr_t)arg);
338 	}
339 
340 	editind++;
341 }
342 
343 static int
344 edit_get(void *hook, char *name)
345 {
346 	int arg = editinfo[editind].default_value;
347 
348 	if (editinfo[editind].can_edit) {
349 		char line[80];
350 		if (fgets(line, sizeof(line), edit_file) == 0)
351 			err(1, "fgets");
352 
353 		line[strlen(line) - 1] = 0;
354 
355 		if (strncmp(name, line, strlen(name)) != 0)
356 			errx(1, "expected \"%s\" and read \"%s\"", name, line);
357 
358 		arg = strtoul(line + strlen(name) + 2, 0, 0);
359 	}
360 
361 	editind++;
362 	return arg;
363 }
364 
365 static void
366 edit_edit(void)
367 {
368 	char *system_line;
369 	char *editor = getenv("EDITOR");
370 	if (!editor)
371 		editor = "vi";
372 
373 	fclose(edit_file);
374 
375 	system_line = malloc(strlen(editor) + strlen(edit_name) + 6);
376 	sprintf(system_line, "%s %s", editor, edit_name);
377 	system(system_line);
378 	free(system_line);
379 
380 	if ((edit_file = fopen(edit_name, "r")) == 0)
381 		err(1, "%s", edit_name);
382 }
383 
384 void
385 mode_edit(struct cam_device *device, int page, int page_control, int dbd,
386 	  int edit, int retry_count, int timeout)
387 {
388 	int i;
389 	u_char data[255];
390 	u_char *mode_pars;
391 	struct mode_header
392 	{
393 		u_char mdl;	/* Mode data length */
394 		u_char medium_type;
395 		u_char dev_spec_par;
396 		u_char bdl;	/* Block descriptor length */
397 	};
398 
399 	struct mode_page_header
400 	{
401 		u_char page_code;
402 		u_char page_length;
403 	};
404 
405 	struct mode_header *mh;
406 	struct mode_page_header *mph;
407 
408 	char *fmt = mode_lookup(page);
409 	if (!fmt && verbose) {
410 		fprintf(stderr,
411 		"No mode data base entry in \"%s\" for page %d; "
412 		" binary %s only.\n",
413 		mode_db, page, (edit ? "edit" : "display"));
414 	}
415 
416 	if (edit) {
417 		if (!fmt)
418 			errx(1, "can't edit without a format");
419 
420 		if (page_control != 0 && page_control != 3)
421 			errx(1, "it only makes sense to edit page 0 "
422 			     "(current) or page 3 (saved values)");
423 
424 		verbose = 1;
425 
426 		mode_sense(device, page, 1, dbd, retry_count, timeout,
427 			   data, sizeof(data));
428 
429 		mh = (struct mode_header *)data;
430 		mph = (struct mode_page_header *)
431 		(((char *)mh) + sizeof(*mh) + mh->bdl);
432 
433 		mode_pars = (char *)mph + sizeof(*mph);
434 
435 		edit_init();
436 		buff_decode_visit(mode_pars, mh->mdl, fmt, edit_check, 0);
437 
438 		mode_sense(device, page, 0, dbd, retry_count, timeout,
439 			   data, sizeof(data));
440 
441 		edit_rewind();
442 		buff_decode_visit(mode_pars, mh->mdl, fmt, edit_defaults, 0);
443 
444 		edit_rewind();
445 		buff_decode_visit(mode_pars, mh->mdl, fmt, edit_report, 0);
446 
447 		edit_edit();
448 
449 		edit_rewind();
450 		buff_encode_visit(mode_pars, mh->mdl, fmt, edit_get, 0);
451 
452 		/* Eliminate block descriptors:
453 		 */
454 		bcopy((char *)mph, ((char *)mh) + sizeof(*mh),
455 		sizeof(*mph) + mph->page_length);
456 
457 		mh->bdl = mh->dev_spec_par = 0;
458 		mph = (struct mode_page_header *) (((char *)mh) + sizeof(*mh));
459 		mode_pars = ((char *)mph) + 2;
460 
461 #if 0
462 		/* Turn this on to see what you're sending to the
463 		 * device:
464 		 */
465 		edit_rewind();
466 		buff_decode_visit(mode_pars, mh->mdl, fmt, arg_put, 0);
467 #endif
468 
469 		edit_done();
470 
471 		/* Make it permanent if pageselect is three.
472 		 */
473 
474 		mph->page_code &= ~0xC0;	/* Clear PS and RESERVED */
475 		mh->mdl = 0;			/* Reserved for mode select */
476 
477 		mode_select(device, (page_control == 3), retry_count,
478 			    timeout, (u_int8_t *)mh, sizeof(*mh) + mh->bdl +
479 			    sizeof(*mph) + mph->page_length);
480 
481 		return;
482 	}
483 
484 	mode_sense(device, page, page_control, dbd, retry_count, timeout,
485 		   data, sizeof(data));
486 
487 	/* Skip over the block descriptors.
488 	 */
489 	mh = (struct mode_header *)data;
490 	mph = (struct mode_page_header *)(((char *)mh) + sizeof(*mh) + mh->bdl);
491 	mode_pars = (char *)mph + sizeof(*mph);
492 
493 	if (!fmt) {
494 		for (i = 0; i < mh->mdl; i++) {
495 			printf("%02x%c",mode_pars[i],
496 			       (((i + 1) % 8) == 0) ? '\n' : ' ');
497 		}
498 		putc('\n', stdout);
499 	} else {
500 		verbose = 1;
501 		buff_decode_visit(mode_pars, mh->mdl, fmt, arg_put, NULL);
502 	}
503 }
504