util.c revision 39214
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	"$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
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();
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();
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 ", (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
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	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
304static void
305edit_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
317static void
318edit_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
327static void
328edit_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
340static int
341edit_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
362static void
363edit_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
381void
382mode_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