util.c revision 56384
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
47static const char rcsid[] =
48  "$FreeBSD: head/sbin/camcontrol/util.c 56384 2000-01-21 23:19:30Z mjacob $";
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
66int verbose = 0;
67
68/* iget: Integer argument callback
69 */
70int
71iget(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 */
93char *
94cget(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 */
116void
117arg_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
164static void
165skipwhite(FILE *f)
166{
167	int c;
168
169skip_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 */
185char *mode_db = "/usr/share/misc/scsi_modes";
186static char *
187mode_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 */
250struct editinfo
251{
252	int can_edit;
253	int default_value;
254} editinfo[64];	/* XXX Bogus fixed size */
255
256static int editind;
257volatile int edit_opened;
258static FILE *edit_file;
259static char edit_name[L_tmpnam];
260
261static inline void
262edit_rewind(void)
263{
264	editind = 0;
265}
266
267static void
268edit_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
291static void
292edit_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
307static void
308edit_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
320static void
321edit_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
330static void
331edit_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
343static int
344edit_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
365static void
366edit_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
384void
385mode_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