This is an overhaul of the mode page handling in camcontrol as well as

related patches. These include:
	* Mode page editting can be scripted. This involves two
	  things: first, if stdin is not a tty, changes are read from
	  stdin rather than invoking $EDITOR. Second, and more
	  importantly, not all modepage entries must be included in the
	  change set. This means that camcontrol can now gracefully handle
	  more intrusive editting from the $EDITOR, including removal or
	  rearrangement of lines. It also means that you can do stuff
	  like:
		# echo "WCE: 1" | camcontrol modepage da3 -m 8 -e
		# newfs /dev/da3
		# echo "WCE: 0" | camcontrol modepage da3 -m 8 -e
	* Range-checking on user-supplied input values. modeedit.c now
	  uses the field width specifiers to determine the maximum
	  allowable value for a field. If the user enters a value larger
	  than the maximum, it clips the value to the max and warns the
	  user. This also involved patching cam_cmdparse.c to be more
	  consistent with regards to the "count" parameter to arg_put
	  (previously is was the length of strings and 1 for all integral
	  types). The cam_cdbparse(3) man page was also updated to reflect
	  the revised semantics.
	* In the process, I removed the 64 entry limit on mode pages (not
	  that we were even close to hitting that limit). This was a nice
	  side-effect of the other changes.
	* Technically, the new mode editting functionality allows editting
	  of character array entries in mode pages (type 'c' or 'z'),
	  however since buff_encode doesn't grok them it is currently
	  useless.
	* Camcontrol gained two new options related to mode pages: -l and
	  -b. The former lists all available mode pages for a given
	  device. The latter forces mode page display in binary format
	  (the default when no mode page definition was found in
	  scsi_modes).
	* Added support for mode page names to scsi_modes. Allows names to
	  be displayed alongside mode numbers in the mode page
	  listing. Updated scsi_modes to use the new functionality. This
	  also adds the semicolon into the scsi_modes syntax as an
	  optional mode page definition terminator. This is needed to name
	  pages without providing a page format definition.
	* Updated scsi_all.h to include a structure describing mode page
	  headers.
	* Added $FreeBSD$ line to scsi_modes.

Inspired by:	dwhite
Reviewed by:	ken
This commit is contained in:
Kelly Yancey 2000-08-08 06:24:17 +00:00
parent 4f0682b0f3
commit 7e32b20d95
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=64382
10 changed files with 908 additions and 823 deletions

View File

@ -444,9 +444,9 @@ function.
.It Fa val
is a void pointer to the value being passed into the function.
.It Fa count
is the number of arguments being passed into the
is the size of the value being passed into the
.Fn arg_put
function. At present this will only be set to 1.
function. The argument format determines the unit of measure.
.It Fa name
This is a text description of the field, if one was provided in the
.Fa fmt .

View File

@ -119,7 +119,8 @@ do_buff_decode(u_int8_t *databuf, size_t len,
if (arg_put) \
(*arg_put)(puthook, (letter == 't' ? \
'b' : letter), \
(void *)((long)(ARG)), 1, field_name); \
(void *)((long)(ARG)), width, \
field_name); \
else \
*(va_arg(ap, int *)) = (ARG); \
assigned++; \

View File

@ -2,7 +2,7 @@
MAINTAINER=ken@FreeBSD.ORG
PROG= camcontrol
SRCS= camcontrol.c modeedit.c
SRCS= camcontrol.c modeedit.c util.c
MAN8= camcontrol.8
SDIR= ${.CURDIR}/../../sys

View File

@ -87,9 +87,9 @@
.Ic modepage
.Op device id
.Op generic args
.Aq Fl m Ar page
.Aq Fl m Ar page \*(Ba Fl l
.Op Fl P Ar pgctl
.Op Fl e
.Op Fl b | Fl e
.Op Fl d
.Nm camcontrol
.Ic cmd
@ -317,11 +317,23 @@ command takes several arguments:
.Bl -tag -width 012345678901
.It Fl d
Disable block descriptors for mode sense.
.It Fl b
Displays mode page data in binary format.
.It Fl e
This flag allows the user to edit values in the mode page.
This flag allows the user to edit values in the mode page. The user may
either edit mode page values with the text editor pointed to by his
.Ev EDITOR
environment variable, or supply mode page values via standard input, using
the same format that
.Nm camcontrol
uses to display mode page values. The editor will be invoked if
.Nm camcontrol
detects that standard input is terminal.
.It Fl l
Lists all available mode pages.
.It Fl m Ar mode_page
This specifies the number of the mode page the user would like to view
and/or edit. This argument is mandatory.
and/or edit. This argument is mandatory unless -l is specified.
.It Fl P Ar pgctl
This allows the user to specify the page control field. Possible values are:
.Bl -tag -width xxx -compact

View File

@ -124,7 +124,7 @@ struct camcontrol_opts option_table[] = {
{"defectlist", CAM_ARG_READ_DEFECTS, readdefect_opts},
{"devlist", CAM_ARG_DEVTREE, NULL},
{"periphlist", CAM_ARG_DEVLIST, NULL},
{"modepage", CAM_ARG_MODE_PAGE, "dem:P:"},
{"modepage", CAM_ARG_MODE_PAGE, "bdelm:P:"},
{"tags", CAM_ARG_TAG, "N:q"},
{"negotiate", CAM_ARG_RATE, negotiate_opts},
{"rate", CAM_ARG_RATE, negotiate_opts},
@ -1554,15 +1554,22 @@ modepage(struct cam_device *device, int argc, char **argv, char *combinedopt,
int retry_count, int timeout)
{
int c, mode_page = -1, page_control = 0;
int binary = 0, list = 0;
while ((c = getopt(argc, argv, combinedopt)) != -1) {
switch(c) {
case 'b':
binary = 1;
break;
case 'd':
arglist |= CAM_ARG_DBD;
break;
case 'e':
arglist |= CAM_ARG_MODE_EDIT;
break;
case 'l':
list = 1;
break;
case 'm':
mode_page = strtol(optarg, NULL, 0);
if (mode_page < 0)
@ -1580,11 +1587,17 @@ modepage(struct cam_device *device, int argc, char **argv, char *combinedopt,
}
}
if (mode_page == -1)
if (mode_page == -1 && list == 0)
errx(1, "you must specify a mode page!");
mode_edit(device, mode_page, page_control, arglist & CAM_ARG_DBD,
arglist & CAM_ARG_MODE_EDIT, retry_count, timeout);
if (list) {
mode_list(device, page_control, arglist & CAM_ARG_DBD,
retry_count, timeout);
} else {
mode_edit(device, mode_page, page_control,
arglist & CAM_ARG_DBD, arglist & CAM_ARG_MODE_EDIT, binary,
retry_count, timeout);
}
}
static int
@ -2975,8 +2988,8 @@ usage(int verbose)
" camcontrol rescan <bus[:target:lun]>\n"
" camcontrol reset <bus[:target:lun]>\n"
" camcontrol defects [dev_id][generic args] <-f format> [-P][-G]\n"
" camcontrol modepage [dev_id][generic args] <-m page> [-P pagectl]\n"
" [-e][-d]\n"
" camcontrol modepage [dev_id][generic args] <-m page | -l>\n"
" [-P pagectl][-e | -b][-d]\n"
" camcontrol cmd [dev_id][generic args] <-c cmd [args]>\n"
" [-i len fmt|-o len fmt [args]]\n"
" camcontrol debug [-I][-T][-S][-c] <all|bus[:target[:lun]]|off>\n"
@ -3022,6 +3035,7 @@ usage(int verbose)
"modepage arguments:\n"
"-m page specify the mode page to view or edit\n"
"-e edit the specified mode page\n"
"-b force view to binary mode\n"
"-d disable block descriptors for mode sense\n"
"-P pgctl page control field 0-3\n"
"defects arguments:\n"

View File

@ -46,7 +46,9 @@ void mode_sense(struct cam_device *device, int mode_page, int page_control,
void mode_select(struct cam_device *device, int save_pages, int retry_count,
int timeout, u_int8_t *data, int datalen);
void mode_edit(struct cam_device *device, int page, int page_control, int dbd,
int edit, int retry_count, int timeout);
int edit, int binary, int retry_count, int timeout);
void mode_list(struct cam_device *device, int page_control, int dbd,
int retry_count, int timeout);
char *cget(void *hook, char *name);
int iget(void *hook, char *name);
void arg_put(void *hook, int letter, void *arg, int count, char *name);

File diff suppressed because it is too large Load Diff

View File

@ -48,22 +48,15 @@ static const char rcsid[] =
"$FreeBSD$";
#endif /* not lint */
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/file.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <cam/cam.h>
#include <cam/cam_ccb.h>
#include <camlib.h>
#include "camcontrol.h"
int verbose = 0;
int verbose;
/* iget: Integer argument callback
*/
@ -157,347 +150,3 @@ arg_put(void *hook, int letter, void *arg, int count, char *name)
if (verbose)
putchar('\n');
}
#define START_ENTRY '{'
#define END_ENTRY '}'
static void
skipwhite(FILE *f)
{
int c;
skip_again:
while (isspace(c = getc(f)))
;
if (c == '#') {
while ((c = getc(f)) != '\n' && c != EOF)
;
goto skip_again;
}
ungetc(c, f);
}
/* mode_lookup: Lookup a format description for a given page.
*/
char *mode_db = "/usr/share/misc/scsi_modes";
static char *
mode_lookup(int page)
{
char *new_db;
FILE *modes;
int match, next, found, c;
static char fmt[4096]; /* XXX This should be with strealloc */
int page_desc;
new_db = getenv("SCSI_MODES");
if (new_db)
mode_db = new_db;
modes = fopen(mode_db, "r");
if (modes == 0)
return 0;
next = 0;
found = 0;
while (!found) {
skipwhite(modes);
if (fscanf(modes, "%i", &page_desc) != 1)
break;
if (page_desc == page)
found = 1;
skipwhite(modes);
if (getc(modes) != START_ENTRY)
errx(1, "expected %c", START_ENTRY);
match = 1;
while (match != 0) {
c = getc(modes);
if (c == EOF) {
warnx("expected %c", END_ENTRY);
}
if (c == START_ENTRY) {
match++;
}
if (c == END_ENTRY) {
match--;
if (match == 0)
break;
}
if (found && c != '\n') {
if (next >= sizeof(fmt))
errx(1, "buffer overflow");
fmt[next++] = (u_char)c;
}
}
}
fmt[next] = 0;
return (found) ? fmt : 0;
}
/* -------- edit: Mode Select Editor ---------
*/
struct editinfo
{
int can_edit;
int default_value;
} editinfo[64]; /* XXX Bogus fixed size */
static int editind;
volatile int edit_opened;
static FILE *edit_file;
static char edit_name[L_tmpnam];
static inline void
edit_rewind(void)
{
editind = 0;
}
static void
edit_done(void)
{
int opened;
sigset_t all, prev;
sigfillset(&all);
(void)sigprocmask(SIG_SETMASK, &all, &prev);
opened = (int)edit_opened;
edit_opened = 0;
(void)sigprocmask(SIG_SETMASK, &prev, 0);
if (opened)
{
if (fclose(edit_file))
warn("%s", edit_name);
if (unlink(edit_name))
warn("%s", edit_name);
}
}
static void
edit_init(void)
{
int fd;
edit_rewind();
strlcpy(edit_name, "/tmp/camXXXXXX", sizeof(edit_name));
if ((fd = mkstemp(edit_name)) == -1)
errx(1, "mkstemp failed");
if ((edit_file = fdopen(fd, "w")) == 0)
err(1, "%s", edit_name);
edit_opened = 1;
atexit(edit_done);
}
static void
edit_check(void *hook, int letter, void *arg, int count, char *name)
{
if (letter != 'i' && letter != 'b')
errx(1, "can't edit format %c", letter);
if (editind >= sizeof(editinfo) / sizeof(editinfo[0]))
errx(1, "edit table overflow");
editinfo[editind].can_edit = (arg != NULL);
editind++;
}
static void
edit_defaults(void *hook, int letter, void *arg, int count, char *name)
{
if (letter != 'i' && letter != 'b')
errx(1, "can't edit format %c", letter);
editinfo[editind].default_value = (intptr_t)arg; /* truncated */
editind++;
}
static void
edit_report(void *hook, int letter, void *arg, int count, char *name)
{
if (editinfo[editind].can_edit) {
if (letter != 'i' && letter != 'b')
errx(1, "can't report format %c", letter);
fprintf(edit_file, "%s: %d\n", name, (intptr_t)arg);
}
editind++;
}
static int
edit_get(void *hook, char *name)
{
int arg = editinfo[editind].default_value;
if (editinfo[editind].can_edit) {
char line[80];
if (fgets(line, sizeof(line), edit_file) == 0)
err(1, "fgets");
line[strlen(line) - 1] = 0;
if (strncmp(name, line, strlen(name)) != 0)
errx(1, "expected \"%s\" and read \"%s\"", name, line);
arg = strtoul(line + strlen(name) + 2, 0, 0);
}
editind++;
return arg;
}
static void
edit_edit(void)
{
char *system_line;
char *editor = getenv("EDITOR");
if (!editor)
editor = "vi";
fclose(edit_file);
system_line = malloc(strlen(editor) + strlen(edit_name) + 6);
sprintf(system_line, "%s %s", editor, edit_name);
system(system_line);
free(system_line);
if ((edit_file = fopen(edit_name, "r")) == 0)
err(1, "%s", edit_name);
}
void
mode_edit(struct cam_device *device, int page, int page_control, int dbd,
int edit, int retry_count, int timeout)
{
int i;
u_char data[255];
u_char *mode_pars;
struct mode_header
{
u_char mdl; /* Mode data length */
u_char medium_type;
u_char dev_spec_par;
u_char bdl; /* Block descriptor length */
};
struct mode_page_header
{
u_char page_code;
u_char page_length;
};
struct mode_header *mh;
struct mode_page_header *mph;
char *fmt = mode_lookup(page);
if (!fmt && verbose) {
fprintf(stderr,
"No mode data base entry in \"%s\" for page %d; "
" binary %s only.\n",
mode_db, page, (edit ? "edit" : "display"));
}
if (edit) {
if (!fmt)
errx(1, "can't edit without a format");
if (page_control != 0 && page_control != 3)
errx(1, "it only makes sense to edit page 0 "
"(current) or page 3 (saved values)");
verbose = 1;
mode_sense(device, page, 1, dbd, retry_count, timeout,
data, sizeof(data));
mh = (struct mode_header *)data;
mph = (struct mode_page_header *)
(((char *)mh) + sizeof(*mh) + mh->bdl);
mode_pars = (char *)mph + sizeof(*mph);
edit_init();
buff_decode_visit(mode_pars, mh->mdl, fmt, edit_check, 0);
mode_sense(device, page, 0, dbd, retry_count, timeout,
data, sizeof(data));
edit_rewind();
buff_decode_visit(mode_pars, mh->mdl, fmt, edit_defaults, 0);
edit_rewind();
buff_decode_visit(mode_pars, mh->mdl, fmt, edit_report, 0);
edit_edit();
edit_rewind();
buff_encode_visit(mode_pars, mh->mdl, fmt, edit_get, 0);
/* Eliminate block descriptors:
*/
bcopy((char *)mph, ((char *)mh) + sizeof(*mh),
sizeof(*mph) + mph->page_length);
mh->bdl = mh->dev_spec_par = 0;
mph = (struct mode_page_header *) (((char *)mh) + sizeof(*mh));
mode_pars = ((char *)mph) + 2;
#if 0
/* Turn this on to see what you're sending to the
* device:
*/
edit_rewind();
buff_decode_visit(mode_pars, mh->mdl, fmt, arg_put, 0);
#endif
edit_done();
/* Make it permanent if pageselect is three.
*/
mph->page_code &= ~0xC0; /* Clear PS and RESERVED */
mh->mdl = 0; /* Reserved for mode select */
mode_select(device, (page_control == 3), retry_count,
timeout, (u_int8_t *)mh, sizeof(*mh) + mh->bdl +
sizeof(*mph) + mph->page_length);
return;
}
mode_sense(device, page, page_control, dbd, retry_count, timeout,
data, sizeof(data));
/* Skip over the block descriptors.
*/
mh = (struct mode_header *)data;
mph = (struct mode_page_header *)(((char *)mh) + sizeof(*mh) + mh->bdl);
mode_pars = (char *)mph + sizeof(*mph);
if (!fmt) {
for (i = 0; i < mh->mdl; i++) {
printf("%02x%c",mode_pars[i],
(((i + 1) % 8) == 0) ? '\n' : ' ');
}
putc('\n', stdout);
} else {
verbose = 1;
buff_decode_visit(mode_pars, mh->mdl, fmt, arg_put, NULL);
}
}

View File

@ -27,13 +27,14 @@
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# $FreeBSD$
#
# The ordering is alphabetical by page name, as it appears in the SCSI spec.
# ALL DEVICE TYPES
# Control mode page:
0x0a {
0x0a "Control Mode Page" {
{Reserved} *t7
{RLEC} t1
{Queue Algorithm Modifier} t4
@ -49,8 +50,7 @@
{Ready AEN Holdoff Period} i2
}
# Disconnect-Reconnect Page:
0x02 {
0x02 "Disconnect-Reconnect Page" {
{Buffer Full Ratio} i1
{Buffer Empty Ratio} i1
{Bus Inactivity Limit} i2
@ -64,8 +64,7 @@
{Reserved} *i1
}
# Peripheral Device Page:
0x09 {
0x09 "Peripheral Device Page" {
{Interface Identifier} i2
{Reserved} *i1
{Reserved} *i1
@ -75,8 +74,7 @@
# DIRECT ACCESS DEVICES
# Caching page:
0x08 {
0x08 "Caching Page" {
{IC} t1
{ABPF} t1
{CAP} t1
@ -93,8 +91,7 @@
{Maximum Pre-fetch Ceiling} i2
}
# Flexible disk page:
0x05 {
0x05 "Flexible Disk Page" {
{Transfer rate} i2
{Number of heads} i1
{Sectors per track} i1
@ -125,8 +122,7 @@
{Reserved} *i1
}
# Format device page:
0x03 {
0x03 "Format Device Page" {
{Tracks per Zone} i2
{Alternate Sectors per Zone} i2
{Alternate Tracks per Zone} i2
@ -143,8 +139,7 @@
{Reserved} *t4
}
# Medium types supported page:
0x0b {
0x0b "Medium Types Supported Page" {
{Reserved} *i1
{Reserved} *i1
{Medium type one supported} i1
@ -154,9 +149,9 @@
}
# Notch page (0x0c)
0x0c "Notch and Partition Page";
# Read-Write Error Recovery Page
0x01 {
0x01 "Read-Write Error Recovery Page" {
{AWRE (Auto Write Reallocation Enbld)} t1
{ARRE (Auto Read Reallocation Enbld)} t1
{TB (Transfer Block)} t1
@ -175,8 +170,7 @@
{Recovery Time Limit} i2
}
# Rigid Disk Drive Geometry Page
0x04 {
0x04 "Rigid Disk Drive Geometry Page" {
{Number of Cylinders} i3
{Number of Heads} i1
{Starting Cylinder-Write Precompensation} i3
@ -192,8 +186,7 @@
{Reserved} *i1
}
# Verify Error Recovery Page
0x07 {
0x07 "Verify Error Recovery Page" {
{Reserved} *t4
{EER} t1
{PER} t1
@ -209,8 +202,7 @@
{Verify Recovery Time Limit} i2
}
# CD-ROM Audio Control Parameters Page
0x0E {
0x0E "CD-ROM Audio Control Parameters Page" {
{Reserved} *t5
{Immed} t1
{SOTC} t1
@ -246,7 +238,7 @@
{Data Buffer Recovery} t1
{Block Identifiers Support} t1
{Report Setmarks} t1
{AAutomatic Velocity Control} t1
{Automatic Velocity Control} t1
{Stop on Consecutive Filemarks} t2
{Recover Buffer Order} t1
{Report Early-Warning} t1
@ -259,3 +251,5 @@
{Select Data Compression Algorithm} i1
{Reserved} *i1
}
0x00 "Vendor-Specific";

View File

@ -677,7 +677,13 @@ struct scsi_mode_header_10
u_int8_t blk_desc_len[2];
};
struct scsi_mode_blk_desc
struct scsi_mode_page_header
{
u_int8_t page_code;
u_int8_t page_length;
};
struct scsi_mode_blk_desc
{
u_int8_t density;
u_int8_t nblocks[3];