util.c revision 56384
192108Sphk/*
292108Sphk * Written By Julian ELischer
392108Sphk * Copyright julian Elischer 1993.
492108Sphk * Permission is granted to use or redistribute this file in any way as long
592108Sphk * as this notice remains. Julian Elischer does not guarantee that this file
692108Sphk * is totally correct for any given task and users of this file must
792108Sphk * accept responsibility for any damage that occurs from the application of this
892108Sphk * file.
992108Sphk *
1092108Sphk * (julian@tfs.com julian@dialix.oz.au)
1192108Sphk *
1292108Sphk * User SCSI hooks added by Peter Dufault:
1392108Sphk *
1492108Sphk * Copyright (c) 1994 HD Associates
1592108Sphk * (contact: dufault@hda.com)
1692108Sphk * All rights reserved.
1792108Sphk *
1892108Sphk * Redistribution and use in source and binary forms, with or without
1992108Sphk * modification, are permitted provided that the following conditions
2092108Sphk * are met:
2192108Sphk * 1. Redistributions of source code must retain the above copyright
2292108Sphk *    notice, this list of conditions and the following disclaimer.
2392108Sphk * 2. Redistributions in binary form must reproduce the above copyright
2492108Sphk *    notice, this list of conditions and the following disclaimer in the
2592108Sphk *    documentation and/or other materials provided with the distribution.
2692108Sphk * 3. The name of HD Associates
2792108Sphk *    may not be used to endorse or promote products derived from this software
2892108Sphk *    without specific prior written permission.
2992108Sphk *
3092108Sphk * THIS SOFTWARE IS PROVIDED BY HD ASSOCIATES ``AS IS'' AND
3192108Sphk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
3292108Sphk * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3392108Sphk * ARE DISCLAIMED.  IN NO EVENT SHALL HD ASSOCIATES BE LIABLE
3492108Sphk * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3592108Sphk * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36116196Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37116196Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38116196Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39104519Sphk * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40104519Sphk * SUCH DAMAGE.
4192108Sphk */
4292108Sphk/*
4392108Sphk * Taken from the original scsi(8) program.
4492108Sphk * from: scsi.c,v 1.17 1998/01/12 07:57:57 charnier Exp $";
4592108Sphk */
46196823Spjd#ifndef lint
47110119Sphkstatic const char rcsid[] =
4892108Sphk  "$FreeBSD: head/sbin/camcontrol/util.c 56384 2000-01-21 23:19:30Z mjacob $";
49223921Sae#endif /* not lint */
50111979Sphk
5192108Sphk#include <ctype.h>
5292108Sphk#include <err.h>
5392108Sphk#include <errno.h>
5492108Sphk#include <string.h>
5592108Sphk#include <stdlib.h>
56112952Sphk#include <stdio.h>
57112476Sphk#include <sys/file.h>
5892108Sphk#include <signal.h>
59219970Smav#include <unistd.h>
60219970Smav
61292348Sken#include <cam/cam.h>
62292348Sken#include <cam/cam_ccb.h>
63219970Smav#include <camlib.h>
64248694Smav#include "camcontrol.h"
65219970Smav
66219970Smavint verbose = 0;
67219970Smav
68219970Smav/* iget: Integer argument callback
69219970Smav */
70260385Sscottlint
71219970Smaviget(void *hook, char *name)
72219970Smav{
7392108Sphk	struct get_hook *h = (struct get_hook *)hook;
74133314Sphk	int arg;
75133314Sphk
76133314Sphk	if (h->got >= h->argc)
77237518Sken	{
7892108Sphk		fprintf(stderr, "Expecting an integer argument.\n");
79141624Sphk		usage(0);
80249507Sivoras		exit(1);
81133318Sphk	}
82133314Sphk	arg = strtol(h->argv[h->got], 0, 0);
83133314Sphk	h->got++;
84133314Sphk
85237518Sken	if (verbose && name && *name)
86133314Sphk		printf("%s: %d\n", name, arg);
8792108Sphk
8892108Sphk	return arg;
89219970Smav}
90227309Sed
91227309Sed/* cget: char * argument callback
92219970Smav */
93115473Sphkchar *
94115473Sphkcget(void *hook, char *name)
95110052Sphk{
96110052Sphk	struct get_hook *h = (struct get_hook *)hook;
97110052Sphk	char *arg;
98226736Spjd
99125975Sphk	if (h->got >= h->argc)
100125975Sphk	{
101110052Sphk		fprintf(stderr, "Expecting a character pointer argument.\n");
102110052Sphk		usage(0);
103110052Sphk		exit(1);
104110052Sphk	}
105110052Sphk	arg = h->argv[h->got];
106226736Spjd	h->got++;
107125975Sphk
108125975Sphk	if (verbose && name)
109110052Sphk		printf("cget: %s: %s", name, arg);
110110052Sphk
11192108Sphk	return arg;
11292108Sphk}
11392108Sphk
11492108Sphk/* arg_put: "put argument" callback
115219970Smav */
11692108Sphkvoid
11792108Sphkarg_put(void *hook, int letter, void *arg, int count, char *name)
11892108Sphk{
11992108Sphk	if (verbose && name && *name)
12092108Sphk		printf("%s:  ", name);
121248694Smav
122219970Smav	switch(letter)
123125539Spjd	{
124125539Spjd		case 'i':
125125539Spjd		case 'b':
126125539Spjd		printf("%d ", (intptr_t)arg);
127125539Spjd		break;
128125539Spjd
129125975Sphk		case 'c':
130125539Spjd		case 'z':
13192108Sphk		{
13292108Sphk			char *p;
13392108Sphk
134110119Sphk			p = malloc(count + 1);
13592108Sphk
136111668Sphk			bzero(p, count +1);
137110119Sphk			strncpy(p, (char *)arg, count);
138111668Sphk			if (letter == 'z')
139166861Sn_hibma			{
140110119Sphk				int i;
141110119Sphk				for (i = count - 1; i >= 0; i--)
142110119Sphk					if (p[i] == ' ')
143251616Smav						p[i] = 0;
144251616Smav					else
145110119Sphk						break;
146105551Sphk			}
147105551Sphk			printf("%s ", p);
148110727Sphk
149110727Sphk			free(p);
150110727Sphk		}
151110727Sphk
152110727Sphk		break;
153252657Ssmh
154252657Ssmh		default:
155252657Ssmh		printf("Unknown format letter: '%c'\n", letter);
156252657Ssmh	}
157249940Ssmh	if (verbose)
158252657Ssmh		putchar('\n');
159249940Ssmh}
160249940Ssmh
161249940Ssmh#define START_ENTRY '{'
162249940Ssmh#define END_ENTRY '}'
16392108Sphk
164111668Sphkstatic void
165110119Sphkskipwhite(FILE *f)
166111668Sphk{
167110119Sphk	int c;
168110119Sphk
169110119Sphkskip_again:
170110119Sphk
171110119Sphk	while (isspace(c = getc(f)))
172219970Smav		;
173219970Smav
174219970Smav	if (c == '#') {
175110119Sphk		while ((c = getc(f)) != '\n' && c != EOF)
17692108Sphk			;
17792108Sphk		goto skip_again;
17892108Sphk	}
17992108Sphk
18092108Sphk	ungetc(c, f);
18195038Sphk}
182219950Smav
18395038Sphk/* mode_lookup: Lookup a format description for a given page.
184104450Sphk */
18595038Sphkchar *mode_db = "/usr/share/misc/scsi_modes";
18695038Sphkstatic char *
187104450Sphkmode_lookup(int page)
188264712Sbdrewery{
189104450Sphk	char *new_db;
190120572Sphk	FILE *modes;
191120572Sphk	int match, next, found, c;
192120572Sphk	static char fmt[4096];	/* XXX This should be with strealloc */
193120572Sphk	int page_desc;
194219950Smav	new_db = getenv("SCSI_MODES");
195219950Smav
196219950Smav	if (new_db)
197219950Smav		mode_db = new_db;
198219950Smav
199140968Sphk	modes = fopen(mode_db, "r");
200140968Sphk	if (modes == 0)
201219950Smav		return 0;
202219950Smav
20395038Sphk	next = 0;
20495038Sphk	found = 0;
20595038Sphk
206219970Smav	while (!found) {
207219970Smav
208219970Smav		skipwhite(modes);
209219970Smav
210219970Smav		if (fscanf(modes, "%i", &page_desc) != 1)
211219970Smav			break;
212219970Smav
213219970Smav		if (page_desc == page)
214219970Smav			found = 1;
215219970Smav
216219970Smav		skipwhite(modes);
217219970Smav		if (getc(modes) != START_ENTRY)
218219970Smav			errx(1, "expected %c", START_ENTRY);
219219970Smav
220219970Smav		match = 1;
221219970Smav		while (match != 0) {
222219970Smav			c = getc(modes);
223219970Smav			if (c == EOF) {
224219970Smav				warnx("expected %c", END_ENTRY);
225219970Smav			}
226219970Smav
227219970Smav			if (c == START_ENTRY) {
228219970Smav				match++;
229219970Smav			}
230219970Smav			if (c == END_ENTRY) {
231219970Smav				match--;
23292108Sphk				if (match == 0)
23392108Sphk					break;
234260385Sscottl			}
235111979Sphk			if (found && c != '\n') {
236219970Smav				if (next >= sizeof(fmt))
23792108Sphk					errx(1, "buffer overflow");
238110720Sphk
239110720Sphk				fmt[next++] = (u_char)c;
240248694Smav			}
241248694Smav		}
24292108Sphk	}
243260385Sscottl	fmt[next] = 0;
244248694Smav
245111979Sphk	return (found) ? fmt : 0;
246111979Sphk}
247111979Sphk
248266608Smav/* -------- edit: Mode Select Editor ---------
249260385Sscottl */
250111979Sphkstruct editinfo
251111979Sphk{
252260385Sscottl	int can_edit;
253112259Sphk	int default_value;
254111979Sphk} editinfo[64];	/* XXX Bogus fixed size */
255260385Sscottl
256260385Sscottlstatic int editind;
257260385Sscottlvolatile int edit_opened;
258260385Sscottlstatic FILE *edit_file;
259260385Sscottlstatic char edit_name[L_tmpnam];
260119660Sphk
261138732Sphkstatic inline void
262119660Sphkedit_rewind(void)
263119660Sphk{
264219970Smav	editind = 0;
265119660Sphk}
266119660Sphk
267248694Smavstatic void
268219970Smavedit_done(void)
269119660Sphk{
270119660Sphk	int opened;
271119660Sphk
272119660Sphk	sigset_t all, prev;
273138732Sphk	sigfillset(&all);
274119660Sphk
275226736Spjd	(void)sigprocmask(SIG_SETMASK, &all, &prev);
276119660Sphk
277119660Sphk	opened = (int)edit_opened;
278292348Sken	edit_opened = 0;
279292348Sken
280292348Sken	(void)sigprocmask(SIG_SETMASK, &prev, 0);
281292348Sken
282292348Sken	if (opened)
283292348Sken	{
284292348Sken		if (fclose(edit_file))
285292348Sken			warn("%s", edit_name);
286292348Sken		if (unlink(edit_name))
287292348Sken			warn("%s", edit_name);
288292348Sken	}
289292348Sken}
290292348Sken
291292348Skenstatic void
29292108Sphkedit_init(void)
293292348Sken{
294292348Sken	int fd;
295292348Sken
296292348Sken	edit_rewind();
297292348Sken	strlcpy(edit_name, "/tmp/camXXXXXX", sizeof(edit_name));
298292348Sken	if ((fd = mkstemp(edit_name)) == -1)
299292348Sken		errx(1, "mkstemp failed");
300292348Sken	if ((edit_file = fdopen(fd, "w")) == 0)
301292348Sken		err(1, "%s", edit_name);
302292348Sken	edit_opened = 1;
303292348Sken
304292348Sken	atexit(edit_done);
305292348Sken}
306292348Sken
307292348Skenstatic void
308292348Skenedit_check(void *hook, int letter, void *arg, int count, char *name)
309292348Sken{
310292348Sken	if (letter != 'i' && letter != 'b')
311292348Sken		errx(1, "can't edit format %c", letter);
312292348Sken
313292348Sken	if (editind >= sizeof(editinfo) / sizeof(editinfo[0]))
314292348Sken		errx(1, "edit table overflow");
315292348Sken
316292348Sken	editinfo[editind].can_edit = (arg != NULL);
317292348Sken	editind++;
318292348Sken}
319292348Sken
320292348Skenstatic void
321292348Skenedit_defaults(void *hook, int letter, void *arg, int count, char *name)
322292348Sken{
323292348Sken	if (letter != 'i' && letter != 'b')
324292348Sken		errx(1, "can't edit format %c", letter);
325292348Sken
326292348Sken	editinfo[editind].default_value = (intptr_t)arg;	/* truncated */
327292348Sken	editind++;
328292348Sken}
329292348Sken
330292348Skenstatic void
331292348Skenedit_report(void *hook, int letter, void *arg, int count, char *name)
332292348Sken{
333292348Sken	if (editinfo[editind].can_edit) {
334292348Sken		if (letter != 'i' && letter != 'b')
335292348Sken			errx(1, "can't report format %c", letter);
336292348Sken
337292348Sken		fprintf(edit_file, "%s:  %d\n", name, (intptr_t)arg);
338292348Sken	}
339292348Sken
340292348Sken	editind++;
341292348Sken}
342292348Sken
343292348Skenstatic int
344292348Skenedit_get(void *hook, char *name)
345292348Sken{
346292348Sken	int arg = editinfo[editind].default_value;
347292348Sken
348292348Sken	if (editinfo[editind].can_edit) {
349292348Sken		char line[80];
350292348Sken		if (fgets(line, sizeof(line), edit_file) == 0)
351292348Sken			err(1, "fgets");
352292348Sken
353292348Sken		line[strlen(line) - 1] = 0;
354292348Sken
355292348Sken		if (strncmp(name, line, strlen(name)) != 0)
356292348Sken			errx(1, "expected \"%s\" and read \"%s\"", name, line);
357292348Sken
358292348Sken		arg = strtoul(line + strlen(name) + 2, 0, 0);
359292348Sken	}
360292348Sken
361292348Sken	editind++;
362292348Sken	return arg;
363292348Sken}
364292348Sken
365292348Skenstatic void
366292348Skenedit_edit(void)
367292348Sken{
368292348Sken	char *system_line;
369292348Sken	char *editor = getenv("EDITOR");
370292348Sken	if (!editor)
371292348Sken		editor = "vi";
372292348Sken
373292348Sken	fclose(edit_file);
374292348Sken
375292348Sken	system_line = malloc(strlen(editor) + strlen(edit_name) + 6);
376292348Sken	sprintf(system_line, "%s %s", editor, edit_name);
377292348Sken	system(system_line);
378292348Sken	free(system_line);
379292348Sken
380292348Sken	if ((edit_file = fopen(edit_name, "r")) == 0)
381292348Sken		err(1, "%s", edit_name);
382292348Sken}
383292348Sken
384292348Skenvoid
385292348Skenmode_edit(struct cam_device *device, int page, int page_control, int dbd,
386292348Sken	  int edit, int retry_count, int timeout)
387292348Sken{
388292348Sken	int i;
389292348Sken	u_char data[255];
390292348Sken	u_char *mode_pars;
391292348Sken	struct mode_header
392292348Sken	{
393292348Sken		u_char mdl;	/* Mode data length */
394292348Sken		u_char medium_type;
395292348Sken		u_char dev_spec_par;
396292348Sken		u_char bdl;	/* Block descriptor length */
397292348Sken	};
398292348Sken
399292348Sken	struct mode_page_header
400292348Sken	{
401292348Sken		u_char page_code;
402292348Sken		u_char page_length;
403292348Sken	};
404292348Sken
405292348Sken	struct mode_header *mh;
406292348Sken	struct mode_page_header *mph;
407292348Sken
408292348Sken	char *fmt = mode_lookup(page);
409292348Sken	if (!fmt && verbose) {
410292348Sken		fprintf(stderr,
411292348Sken		"No mode data base entry in \"%s\" for page %d; "
412292348Sken		" binary %s only.\n",
413292348Sken		mode_db, page, (edit ? "edit" : "display"));
414292348Sken	}
415292348Sken
416292348Sken	if (edit) {
417292348Sken		if (!fmt)
41892108Sphk			errx(1, "can't edit without a format");
41992108Sphk
420110720Sphk		if (page_control != 0 && page_control != 3)
42192108Sphk			errx(1, "it only makes sense to edit page 0 "
422219970Smav			     "(current) or page 3 (saved values)");
42392403Sphk
424273767Smav		verbose = 1;
42592108Sphk
426248694Smav		mode_sense(device, page, 1, dbd, retry_count, timeout,
427219970Smav			   data, sizeof(data));
428115214Sphk
429143791Sphk		mh = (struct mode_header *)data;
430143791Sphk		mph = (struct mode_page_header *)
431104609Sphk		(((char *)mh) + sizeof(*mh) + mh->bdl);
43292108Sphk
433104609Sphk		mode_pars = (char *)mph + sizeof(*mph);
434110119Sphk
435226737Spjd		edit_init();
436104609Sphk		buff_decode_visit(mode_pars, mh->mdl, fmt, edit_check, 0);
437104609Sphk
438104609Sphk		mode_sense(device, page, 0, dbd, retry_count, timeout,
43992108Sphk			   data, sizeof(data));
44092108Sphk
441292348Sken		edit_rewind();
442292348Sken		buff_decode_visit(mode_pars, mh->mdl, fmt, edit_defaults, 0);
443292348Sken
444110720Sphk		edit_rewind();
445110720Sphk		buff_decode_visit(mode_pars, mh->mdl, fmt, edit_report, 0);
44692108Sphk
447110477Sphk		edit_edit();
448110477Sphk
449110477Sphk		edit_rewind();
450110477Sphk		buff_encode_visit(mode_pars, mh->mdl, fmt, edit_get, 0);
451292348Sken
452292348Sken		/* Eliminate block descriptors:
453292348Sken		 */
454273767Smav		bcopy((char *)mph, ((char *)mh) + sizeof(*mh),
455110720Sphk		sizeof(*mph) + mph->page_length);
456110720Sphk
457110720Sphk		mh->bdl = mh->dev_spec_par = 0;
458110720Sphk		mph = (struct mode_page_header *) (((char *)mh) + sizeof(*mh));
459110720Sphk		mode_pars = ((char *)mph) + 2;
460110720Sphk
461110720Sphk#if 0
462110720Sphk		/* Turn this on to see what you're sending to the
463110720Sphk		 * device:
464110720Sphk		 */
465110720Sphk		edit_rewind();
466110720Sphk		buff_decode_visit(mode_pars, mh->mdl, fmt, arg_put, 0);
467260385Sscottl#endif
468150759Stegge
469260385Sscottl		edit_done();
470110720Sphk
471110720Sphk		/* Make it permanent if pageselect is three.
472110720Sphk		 */
473292348Sken
474292348Sken		mph->page_code &= ~0xC0;	/* Clear PS and RESERVED */
475292348Sken		mh->mdl = 0;			/* Reserved for mode select */
476292348Sken
477110720Sphk		mode_select(device, (page_control == 3), retry_count,
478110720Sphk			    timeout, (u_int8_t *)mh, sizeof(*mh) + mh->bdl +
479292348Sken			    sizeof(*mph) + mph->page_length);
480292348Sken
48192108Sphk		return;
48292108Sphk	}
483223089Sgibbs
484223089Sgibbs	mode_sense(device, page, page_control, dbd, retry_count, timeout,
485223089Sgibbs		   data, sizeof(data));
486223089Sgibbs
487223089Sgibbs	/* Skip over the block descriptors.
488223089Sgibbs	 */
489223089Sgibbs	mh = (struct mode_header *)data;
490223089Sgibbs	mph = (struct mode_page_header *)(((char *)mh) + sizeof(*mh) + mh->bdl);
491223089Sgibbs	mode_pars = (char *)mph + sizeof(*mph);
492216794Skib
493216794Skib	if (!fmt) {
49492403Sphk		for (i = 0; i < mh->mdl; i++) {
495216794Skib			printf("%02x%c",mode_pars[i],
496216794Skib			       (((i + 1) % 8) == 0) ? '\n' : ' ');
497216794Skib		}
498103714Sphk		putc('\n', stdout);
49992403Sphk	} else {
50098066Sphk		verbose = 1;
50194287Sphk		buff_decode_visit(mode_pars, mh->mdl, fmt, arg_put, NULL);
502169285Spjd	}
503169285Spjd}
504271238Ssmh