summaryrefslogtreecommitdiffstats
path: root/sbin
diff options
context:
space:
mode:
authorkbyanc <kbyanc@FreeBSD.org>2000-08-08 06:24:17 +0000
committerkbyanc <kbyanc@FreeBSD.org>2000-08-08 06:24:17 +0000
commit8f0afc6b0aa752cb23dfcf91f2285bf6c825405d (patch)
tree034cb732a278c59e0304f2f93947626f19ea5385 /sbin
parent18a0d975369707cba8c9deeb3de4db05dafb9487 (diff)
downloadFreeBSD-src-8f0afc6b0aa752cb23dfcf91f2285bf6c825405d.zip
FreeBSD-src-8f0afc6b0aa752cb23dfcf91f2285bf6c825405d.tar.gz
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
Diffstat (limited to 'sbin')
-rw-r--r--sbin/camcontrol/Makefile2
-rw-r--r--sbin/camcontrol/camcontrol.820
-rw-r--r--sbin/camcontrol/camcontrol.c26
-rw-r--r--sbin/camcontrol/camcontrol.h4
-rw-r--r--sbin/camcontrol/modeedit.c1153
-rw-r--r--sbin/camcontrol/util.c357
6 files changed, 823 insertions, 739 deletions
diff --git a/sbin/camcontrol/Makefile b/sbin/camcontrol/Makefile
index e91c205..39763ed 100644
--- a/sbin/camcontrol/Makefile
+++ b/sbin/camcontrol/Makefile
@@ -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
diff --git a/sbin/camcontrol/camcontrol.8 b/sbin/camcontrol/camcontrol.8
index 6d2c59f..925cb87 100644
--- a/sbin/camcontrol/camcontrol.8
+++ b/sbin/camcontrol/camcontrol.8
@@ -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
diff --git a/sbin/camcontrol/camcontrol.c b/sbin/camcontrol/camcontrol.c
index fcacca4..c7e96cd 100644
--- a/sbin/camcontrol/camcontrol.c
+++ b/sbin/camcontrol/camcontrol.c
@@ -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"
diff --git a/sbin/camcontrol/camcontrol.h b/sbin/camcontrol/camcontrol.h
index 4218576..eebbe85 100644
--- a/sbin/camcontrol/camcontrol.h
+++ b/sbin/camcontrol/camcontrol.h
@@ -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);
diff --git a/sbin/camcontrol/modeedit.c b/sbin/camcontrol/modeedit.c
index 32f5c78..faf592f 100644
--- a/sbin/camcontrol/modeedit.c
+++ b/sbin/camcontrol/modeedit.c
@@ -1,63 +1,50 @@
-/*
- * Written By Julian ELischer
- * Copyright julian Elischer 1993.
- * Permission is granted to use or redistribute this file in any way as long
- * as this notice remains. Julian Elischer does not guarantee that this file
- * is totally correct for any given task and users of this file must
- * accept responsibility for any damage that occurs from the application of this
- * file.
- *
- * (julian@tfs.com julian@dialix.oz.au)
- *
- * User SCSI hooks added by Peter Dufault:
- *
- * Copyright (c) 1994 HD Associates
- * (contact: dufault@hda.com)
+/*-
+ * Copyright (c) 2000 Kelly Yancey <kbyanc@posi.net>
+ * Derived from work done by Julian Elischer <julian@tfs.com,
+ * julian@dialix.oz.au>, 1993, and Peter Dufault <dufault@hda.com>, 1994.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
+ * notice, this list of conditions and the following disclaimer,
+ * without modification, immediately at the beginning of the file.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The name of HD Associates
- * may not be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY HD ASSOCIATES ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL HD ASSOCIATES BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * 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.
- */
-/*
- * Taken from the original scsi(8) program.
- * from: scsi.c,v 1.17 1998/01/12 07:57:57 charnier Exp $";
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 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.
*/
+
#ifndef lint
static const char rcsid[] =
"$FreeBSD$";
#endif /* not lint */
+#include <sys/queue.h>
+#include <sys/types.h>
+
+#include <assert.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
-#include <string.h>
#include <stdlib.h>
+#include <string.h>
#include <stdio.h>
-#include <sys/file.h>
-#include <signal.h>
+#include <sysexits.h>
#include <unistd.h>
+#include <cam/scsi/scsi_all.h>
#include <cam/cam.h>
#include <cam/cam_ccb.h>
#include <camlib.h>
@@ -65,439 +52,859 @@ static const char rcsid[] =
int verbose = 0;
-/* iget: Integer argument callback
- */
-int
-iget(void *hook, char *name)
-{
- struct get_hook *h = (struct get_hook *)hook;
- int arg;
-
- if (h->got >= h->argc)
- {
- fprintf(stderr, "Expecting an integer argument.\n");
- usage(0);
- exit(1);
- }
- arg = strtol(h->argv[h->got], 0, 0);
- h->got++;
+#define DEFAULT_SCSI_MODE_DB "/usr/share/misc/scsi_modes"
+#define DEFAULT_EDITOR "vi"
+#define MAX_FORMAT_SPEC 4096 /* Max CDB format specifier. */
+#define MAX_PAGENUM_LEN 10 /* Max characters in page num. */
+#define MAX_PAGENAME_LEN 64 /* Max characters in page name. */
+#define PAGEDEF_START '{' /* Page definition delimiter. */
+#define PAGEDEF_END '}' /* Page definition delimiter. */
+#define PAGENAME_START '"' /* Page name delimiter. */
+#define PAGENAME_END '"' /* Page name delimiter. */
+#define PAGEENTRY_END ';' /* Page entry terminator (optional). */
+#define MAX_COMMAND_SIZE 255 /* Mode/Log sense data buffer size. */
+
+
+/* Macros for working with mode pages. */
+#define MODE_PAGE_HEADER(mh) \
+ (struct scsi_mode_page_header *)find_mode_page_6(mh)
+
+#define MODE_PAGE_DATA(mph) \
+ (u_int8_t *)(mph) + sizeof(struct scsi_mode_page_header)
+
+
+struct editentry {
+ STAILQ_ENTRY(editentry) link;
+ char *name;
+ char type;
+ int editable;
+ int size;
+ union {
+ int ivalue;
+ char *svalue;
+ } value;
+};
+STAILQ_HEAD(, editentry) editlist; /* List of page entries. */
+int editlist_changed = 0; /* Whether any entries were changed. */
+
+struct pagename {
+ SLIST_ENTRY(pagename) link;
+ int pagenum;
+ char *name;
+};
+SLIST_HEAD(, pagename) namelist; /* Page number to name mappings. */
+
+static char format[MAX_FORMAT_SPEC]; /* Buffer for scsi cdb format def. */
+
+static FILE *edit_file = NULL; /* File handle for edit file. */
+static char edit_path[] = "/tmp/camXXXXXX";
+
+
+/* Function prototypes. */
+static void editentry_create(void *hook, int letter, void *arg,
+ int count, char *name);
+static void editentry_update(void *hook, int letter, void *arg,
+ int count, char *name);
+static int editentry_save(void *hook, char *name);
+static struct editentry *editentry_lookup(char *name);
+static int editentry_set(char *name, char *newvalue,
+ int editonly);
+static void editlist_populate(struct cam_device *device,
+ int modepage, int page_control,
+ int dbd, int retries, int timeout);
+static void editlist_save(struct cam_device *device, int modepage,
+ int page_control, int dbd, int retries,
+ int timeout);
+static void nameentry_create(int pagenum, char *name);
+static struct pagename *nameentry_lookup(int pagenum);
+static int load_format(char *pagedb_path, int page);
+static int modepage_write(FILE *file, int editonly);
+static int modepage_read(FILE *file);
+static void modepage_edit(void);
+static void modepage_dump(struct cam_device *device, int page,
+ int page_control, int dbd, int retries,
+ int timeout);
+static void cleanup_editfile(void);
+void mode_edit(struct cam_device *device, int page,
+ int page_control, int dbd, 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);
+
+
+#define returnerr(code) do { \
+ errno = code; \
+ return (-1); \
+} while (0)
+
+
+#define RTRIM(string) do { \
+ register int _length; \
+ while (isspace(string[_length = strlen(string) - 1])) \
+ string[_length] = '\0'; \
+} while (0)
- if (verbose && name && *name)
- printf("%s: %d\n", name, arg);
- return arg;
-}
-
-/* cget: char * argument callback
- */
-char *
-cget(void *hook, char *name)
+static void
+editentry_create(void *hook, int letter, void *arg, int count, char *name)
{
- struct get_hook *h = (struct get_hook *)hook;
- char *arg;
-
- if (h->got >= h->argc)
- {
- fprintf(stderr, "Expecting a character pointer argument.\n");
- usage(0);
- exit(1);
- }
- arg = h->argv[h->got];
- h->got++;
+ struct editentry *newentry; /* Buffer to hold new entry. */
- if (verbose && name)
- printf("cget: %s: %s", name, arg);
+ /* Allocate memory for the new entry and a copy of the entry name. */
+ if ((newentry = malloc(sizeof(struct editentry))) == NULL ||
+ (newentry->name = strdup(name)) == NULL)
+ err(EX_OSERR, NULL);
- return arg;
+ /* Trim any trailing whitespace for the entry name. */
+ RTRIM(newentry->name);
+
+ newentry->editable = (arg != NULL);
+ newentry->type = letter;
+ newentry->size = count; /* Placeholder; not accurate. */
+ newentry->value.svalue = NULL;
+
+ STAILQ_INSERT_TAIL(&editlist, newentry, link);
}
-/* arg_put: "put argument" callback
- */
-void
-arg_put(void *hook, int letter, void *arg, int count, char *name)
+static void
+editentry_update(void *hook, int letter, void *arg, int count, char *name)
{
- if (verbose && name && *name)
- printf("%s: ", name);
-
- switch(letter)
- {
- case 'i':
- case 'b':
- printf("%d ", (intptr_t)arg);
- break;
+ struct editentry *dest; /* Buffer to hold entry to update. */
- case 'c':
- case 'z':
- {
- char *p;
-
- p = malloc(count + 1);
-
- bzero(p, count +1);
- strncpy(p, (char *)arg, count);
- if (letter == 'z')
- {
- int i;
- for (i = count - 1; i >= 0; i--)
- if (p[i] == ' ')
- p[i] = 0;
- else
- break;
- }
- printf("%s ", p);
+ dest = editentry_lookup(name);
+ assert(dest != NULL);
- free(p);
- }
+ dest->type = letter;
+ dest->size = count; /* We get the real size now. */
+ switch (dest->type) {
+ case 'i': /* Byte-sized integral type. */
+ case 'b': /* Bit-sized integral types. */
+ case 't':
+ dest->value.ivalue = (intptr_t)arg;
break;
- default:
- printf("Unknown format letter: '%c'\n", letter);
+ case 'c': /* Character array. */
+ case 'z': /* Null-padded string. */
+ editentry_set(name, (char *)arg, 0);
+ break;
+ default:
+ /* NOTREACHED */
}
- if (verbose)
- putchar('\n');
}
-#define START_ENTRY '{'
-#define END_ENTRY '}'
-
-static void
-skipwhite(FILE *f)
+static int
+editentry_save(void *hook, char *name)
{
- int c;
+ struct editentry *src; /* Entry value to save. */
+
+ src = editentry_lookup(name);
+ assert(src != NULL);
-skip_again:
+ switch (src->type) {
+ case 'i': /* Byte-sized integral type. */
+ case 'b': /* Bit-sized integral types. */
+ case 't':
+ return (src->value.ivalue);
+ /* NOTREACHED */
- while (isspace(c = getc(f)))
- ;
+ case 'c': /* Character array. */
+ case 'z': /* Null-padded string. */
+ return ((intptr_t)src->value.svalue);
+ /* NOTREACHED */
- if (c == '#') {
- while ((c = getc(f)) != '\n' && c != EOF)
- ;
- goto skip_again;
+ default:
+ /* NOTREACHED */
}
- ungetc(c, f);
+ return (0); /* This should never happen. */
}
-/* mode_lookup: Lookup a format description for a given page.
- */
-char *mode_db = "/usr/share/misc/scsi_modes";
-static char *
-mode_lookup(int page)
+static struct editentry *
+editentry_lookup(char *name)
{
- 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");
+ struct editentry *scan;
- if (new_db)
- mode_db = new_db;
+ assert(name != NULL);
- modes = fopen(mode_db, "r");
- if (modes == 0)
- return 0;
+ STAILQ_FOREACH(scan, &editlist, link) {
+ if (strcasecmp(scan->name, name) == 0)
+ return (scan);
+ }
- next = 0;
- found = 0;
+ /* Not found during list traversal. */
+ return (NULL);
+}
- while (!found) {
+static int
+editentry_set(char *name, char *newvalue, int editonly)
+{
+ struct editentry *dest; /* Modepage entry to update. */
+ char *cval; /* Pointer to new string value. */
+ char *convertend; /* End-of-conversion pointer. */
+ int ival; /* New integral value. */
+ int resolution; /* Resolution in bits for integer conversion. */
- skipwhite(modes);
+/*
+ * Macro to determine the maximum value of the given size for the current
+ * resolution.
+ * XXX Lovely x86's optimize out the case of shifting by 32 and gcc doesn't
+ * currently workaround it (even for int64's), so we have to kludge it.
+ */
+#define RESOLUTION_MAX(size) ((resolution * (size) == 32)? \
+ 0xffffffff: 1 << (resolution * (size)) - 1)
+
+ assert(newvalue != NULL);
+ if (*newvalue == '\0')
+ return (0); /* Nothing to do. */
+
+ if ((dest = editentry_lookup(name)) == NULL)
+ returnerr(ENOENT);
+ if (!dest->editable && editonly)
+ returnerr(EPERM);
+
+ switch (dest->type) {
+ case 'i': /* Byte-sized integral type. */
+ case 'b': /* Bit-sized integral types. */
+ case 't':
+ /* Convert the value string to an integer. */
+ resolution = (dest->type == 'i')? 8: 1;
+ ival = (int)strtol(newvalue, &convertend, 0);
+ if (*convertend != '\0')
+ returnerr(EINVAL);
+ if (ival > RESOLUTION_MAX(dest->size) || ival < 0) {
+ int newival = (ival < 0)? 0: RESOLUTION_MAX(dest->size);
+ warnx("value %d is out of range for entry %s; clipping "
+ "to %d", ival, name, newival);
+ ival = newival;
+ }
+ if (dest->value.ivalue != ival)
+ editlist_changed = 1;
+ dest->value.ivalue = ival;
+ break;
- if (fscanf(modes, "%i", &page_desc) != 1)
+ case 'c': /* Character array. */
+ case 'z': /* Null-padded string. */
+ if ((cval = malloc(dest->size + 1)) == NULL)
+ err(EX_OSERR, NULL);
+ bzero(cval, dest->size + 1);
+ strncpy(cval, newvalue, dest->size);
+ if (dest->type == 'z') {
+ /* Convert trailing spaces to nulls. */
+ char *convertend;
+
+ for (convertend = cval + dest->size;
+ convertend >= cval; convertend--) {
+ if (*convertend == ' ')
+ *convertend = '\0';
+ else if (*convertend != '\0')
+ break;
+ }
+ }
+ if (strncmp(dest->value.svalue, cval, dest->size) == 0) {
+ /* Nothing changed, free the newly allocated string. */
+ free(cval);
break;
+ }
+ if (dest->value.svalue != NULL) {
+ /* Free the current string buffer. */
+ free(dest->value.svalue);
+ dest->value.svalue = NULL;
+ }
+ dest->value.svalue = cval;
+ editlist_changed = 1;
+ break;
- if (page_desc == page)
- found = 1;
+ default:
+ /* NOTREACHED */
+ }
- skipwhite(modes);
- if (getc(modes) != START_ENTRY)
- errx(1, "expected %c", START_ENTRY);
+ return (0);
+#undef RESOLUTION_MAX
+}
- match = 1;
- while (match != 0) {
- c = getc(modes);
- if (c == EOF) {
- warnx("expected %c", END_ENTRY);
- }
+static void
+nameentry_create(int pagenum, char *name) {
+ struct pagename *newentry;
- 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");
+ if (pagenum < 0 || name == NULL || name[0] == '\0')
+ return;
- fmt[next++] = (u_char)c;
- }
- }
- }
- fmt[next] = 0;
+ /* Allocate memory for the new entry and a copy of the entry name. */
+ if ((newentry = malloc(sizeof(struct pagename))) == NULL ||
+ (newentry->name = strdup(name)) == NULL)
+ err(EX_OSERR, NULL);
- return (found) ? fmt : 0;
+ /* Trim any trailing whitespace for the page name. */
+ RTRIM(newentry->name);
+
+ newentry->pagenum = pagenum;
+ SLIST_INSERT_HEAD(&namelist, newentry, link);
}
-/* -------- edit: Mode Select Editor ---------
- */
-struct editinfo
-{
- int can_edit;
- int default_value;
-} editinfo[64]; /* XXX Bogus fixed size */
+static struct pagename *
+nameentry_lookup(int pagenum) {
+ struct pagename *scan;
-static int editind;
-volatile int edit_opened;
-static FILE *edit_file;
-static char edit_name[L_tmpnam];
+ SLIST_FOREACH(scan, &namelist, link) {
+ if (pagenum == scan->pagenum)
+ return (scan);
+ }
-static inline void
-edit_rewind(void)
-{
- editind = 0;
+ /* Not found during list traversal. */
+ return (NULL);
}
-static void
-edit_done(void)
+static int
+load_format(char *pagedb_path, int page)
{
- int opened;
+ FILE *pagedb;
+ char str_pagenum[MAX_PAGENUM_LEN];
+ char str_pagename[MAX_PAGENAME_LEN];
+ int pagenum;
+ int depth; /* Quoting depth. */
+ int found;
+ int lineno;
+ enum { LOCATE, PAGENAME, PAGEDEF } state;
+ char c;
+
+#define SETSTATE_LOCATE do { \
+ str_pagenum[0] = '\0'; \
+ str_pagename[0] = '\0'; \
+ pagenum = -1; \
+ state = LOCATE; \
+} while (0)
+
+#define SETSTATE_PAGENAME do { \
+ str_pagename[0] = '\0'; \
+ state = PAGENAME; \
+} while (0)
+
+#define SETSTATE_PAGEDEF do { \
+ format[0] = '\0'; \
+ state = PAGEDEF; \
+} while (0)
+
+#define UPDATE_LINENO do { \
+ if (c == '\n') \
+ lineno++; \
+} while (0)
+
+#define BUFFERFULL(buffer) (strlen(buffer) + 1 >= sizeof(buffer))
+
+ if ((pagedb = fopen(pagedb_path, "r")) == NULL)
+ returnerr(ENOENT);
+
+ SLIST_INIT(&namelist);
+
+ depth = 0;
+ lineno = 0;
+ found = 0;
+ SETSTATE_LOCATE;
+ while ((c = fgetc(pagedb)) != EOF) {
+
+ /* Keep a line count to make error messages more useful. */
+ UPDATE_LINENO;
+
+ /* Skip over comments anywhere in the mode database. */
+ if (c == '#') {
+ do {
+ c = fgetc(pagedb);
+ } while (c != '\n' && c != EOF);
+ UPDATE_LINENO;
+ continue;
+ }
- sigset_t all, prev;
- sigfillset(&all);
+ /* Strip out newline characters. */
+ if (c == '\n')
+ continue;
+
+ /* Keep track of the nesting depth for braces. */
+ if (c == PAGEDEF_START)
+ depth++;
+ else if (c == PAGEDEF_END) {
+ depth--;
+ if (depth < 0) {
+ errx(EX_OSFILE, "%s:%d: %s", pagedb_path,
+ lineno, "mismatched bracket");
+ }
+ }
- (void)sigprocmask(SIG_SETMASK, &all, &prev);
+ switch (state) {
+ case LOCATE:
+ /*
+ * Locate the page the user is interested in, skipping
+ * all others.
+ */
+ if (isspace(c)) {
+ /* Ignore all whitespace between pages. */
+ break;
+ } else if (depth == 0 && c == PAGEENTRY_END) {
+ /*
+ * A page entry terminator will reset page
+ * scanning (useful for assigning names to
+ * modes without providing a mode definition).
+ */
+ /* Record the name of this page. */
+ pagenum = strtol(str_pagenum, NULL, 0);
+ nameentry_create(pagenum, str_pagename);
+ SETSTATE_LOCATE;
+ } else if (depth == 0 && c == PAGENAME_START) {
+ SETSTATE_PAGENAME;
+ } else if (c == PAGEDEF_START) {
+ pagenum = strtol(str_pagenum, NULL, 0);
+ if (depth == 1) {
+ /* Record the name of this page. */
+ nameentry_create(pagenum, str_pagename);
+ /*
+ * Only record the format if this is
+ * the page we are interested in.
+ */
+ if (page == pagenum && !found)
+ SETSTATE_PAGEDEF;
+ }
+ } else if (c == PAGEDEF_END) {
+ /* Reset the processor state. */
+ SETSTATE_LOCATE;
+ } else if (depth == 0 && ! BUFFERFULL(str_pagenum)) {
+ strncat(str_pagenum, &c, 1);
+ } else if (depth == 0) {
+ errx(EX_OSFILE, "%s:%d: %s %d %s", pagedb_path,
+ lineno, "page identifier exceeds",
+ sizeof(str_pagenum) - 1, "characters");
+ }
+ break;
- opened = (int)edit_opened;
- edit_opened = 0;
+ case PAGENAME:
+ if (c == PAGENAME_END) {
+ /*
+ * Return to LOCATE state without resetting the
+ * page number buffer.
+ */
+ state = LOCATE;
+ } else if (! BUFFERFULL(str_pagename)) {
+ strncat(str_pagename, &c, 1);
+ } else {
+ errx(EX_OSFILE, "%s:%d: %s %d %s", pagedb_path,
+ lineno, "page name exceeds",
+ sizeof(str_pagenum) - 1, "characters");
+ }
+ break;
- (void)sigprocmask(SIG_SETMASK, &prev, 0);
+ case PAGEDEF:
+ /*
+ * Transfer the page definition into a format buffer
+ * suitable for use with CDB encoding/decoding routines.
+ */
+ if (depth == 0) {
+ found = 1;
+ SETSTATE_LOCATE;
+ } else if (! BUFFERFULL(format)) {
+ strncat(format, &c, 1);
+ } else {
+ errx(EX_OSFILE, "%s:%d: %s %d %s", pagedb_path,
+ lineno, "page definition exceeds",
+ sizeof(format) - 1, "characters");
+ }
+ break;
- if (opened)
- {
- if (fclose(edit_file))
- warn("%s", edit_name);
- if (unlink(edit_name))
- warn("%s", edit_name);
+ default:
+ /* NOTREACHED */
+ }
+
+ /* Repeat processing loop with next character. */
}
-}
-static void
-edit_init(void)
-{
- int fd;
+ if (ferror(pagedb))
+ err(EX_OSFILE, "%s", pagedb_path);
+
+ /* Close the SCSI page database. */
+ fclose(pagedb);
- 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;
+ if (!found) /* Never found a matching page. */
+ returnerr(ESRCH);
- atexit(edit_done);
+ return (0);
}
static void
-edit_check(void *hook, int letter, void *arg, int count, char *name)
+editlist_populate(struct cam_device *device, int modepage, int page_control,
+ int dbd, int retries, int timeout)
{
- 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++;
+ u_int8_t data[MAX_COMMAND_SIZE];/* Buffer to hold sense data. */
+ u_int8_t *mode_pars; /* Pointer to modepage params. */
+ struct scsi_mode_header_6 *mh; /* Location of mode header. */
+ struct scsi_mode_page_header *mph;
+
+ STAILQ_INIT(&editlist);
+
+ /* Fetch changeable values; use to build initial editlist. */
+ mode_sense(device, modepage, 1, dbd, retries, timeout, data,
+ sizeof(data));
+
+ mh = (struct scsi_mode_header_6 *)data;
+ mph = MODE_PAGE_HEADER(mh);
+ mode_pars = MODE_PAGE_DATA(mph);
+
+ /* Decode the value data, creating edit_entries for each value. */
+ buff_decode_visit(mode_pars, mh->data_length, format,
+ editentry_create, 0);
+
+ /* Fetch the current/saved values; use to set editentry values. */
+ mode_sense(device, modepage, page_control, dbd, retries, timeout, data,
+ sizeof(data));
+ buff_decode_visit(mode_pars, mh->data_length, format,
+ editentry_update, 0);
}
static void
-edit_defaults(void *hook, int letter, void *arg, int count, char *name)
+editlist_save(struct cam_device *device, int modepage, int page_control,
+ int dbd, int retries, int timeout)
{
- if (letter != 'i' && letter != 'b')
- errx(1, "can't edit format %c", letter);
+ u_int8_t data[MAX_COMMAND_SIZE];/* Buffer to hold sense data. */
+ u_int8_t *mode_pars; /* Pointer to modepage params. */
+ struct scsi_mode_header_6 *mh; /* Location of mode header. */
+ struct scsi_mode_page_header *mph;
- editinfo[editind].default_value = (intptr_t)arg; /* truncated */
- editind++;
+ /* Make sure that something changed before continuing. */
+ if (! editlist_changed)
+ return;
+
+ /*
+ * Preload the CDB buffer with the current mode page data.
+ * XXX If buff_encode_visit would return the number of bytes encoded
+ * we *should* use that to build a header from scratch. As it is
+ * now, we need mode_sense to find out the page length.
+ */
+ mode_sense(device, modepage, page_control, dbd, retries, timeout, data,
+ sizeof(data));
+
+ /* Initial headers & offsets. */
+ mh = (struct scsi_mode_header_6 *)data;
+ mph = MODE_PAGE_HEADER(mh);
+ mode_pars = MODE_PAGE_DATA(mph);
+
+ /* Encode the value data to be passed back to the device. */
+ buff_encode_visit(mode_pars, mh->data_length, format,
+ editentry_save, 0);
+
+ /* Eliminate block descriptors. */
+ bcopy(mph, ((u_int8_t *)mh) + sizeof(*mh),
+ sizeof(*mph) + mph->page_length);
+
+ /* Recalculate headers & offsets. */
+ mh->blk_desc_len = 0; /* No block descriptors. */
+ mh->dev_spec = 0; /* Clear device-specific parameters. */
+ mph = MODE_PAGE_HEADER(mh);
+ mode_pars = MODE_PAGE_DATA(mph);
+
+ mph->page_code &= SMS_PAGE_CODE;/* Isolate just the page code. */
+ mh->data_length = 0; /* Reserved for MODE SELECT command. */
+
+ /*
+ * Write the changes back to the device. If the user editted control
+ * page 3 (saved values) then request the changes be permanently
+ * recorded.
+ */
+ mode_select(device, (page_control == SMS_PAGE_CTRL_SAVED), retries,
+ timeout, (u_int8_t *)mh, sizeof(*mh) + mh->blk_desc_len +
+ sizeof(*mph) + mph->page_length);
}
-static void
-edit_report(void *hook, int letter, void *arg, int count, char *name)
+static int
+modepage_write(FILE *file, int editonly)
{
- 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);
+ struct editentry *scan;
+ int written = 0;
+
+ STAILQ_FOREACH(scan, &editlist, link) {
+ if (scan->editable || !editonly) {
+ written++;
+ if (scan->type == 'c' || scan->type == 'z') {
+ fprintf(file, "%s: %s\n", scan->name,
+ scan->value.svalue);
+ } else {
+ fprintf(file, "%s: %d\n", scan->name,
+ scan->value.ivalue);
+ }
+ }
}
-
- editind++;
+ return (written);
}
static int
-edit_get(void *hook, char *name)
+modepage_read(FILE *file)
{
- 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);
+ char *buffer; /* Pointer to dynamic line buffer. */
+ char *line; /* Pointer to static fgetln buffer. */
+ char *name; /* Name portion of the line buffer. */
+ char *value; /* Value portion of line buffer. */
+ int length; /* Length of static fgetln buffer. */
+
+#define ABORT_READ(message, param) do { \
+ warnx(message, param); \
+ free(buffer); \
+ returnerr(EAGAIN); \
+} while (0)
+
+ while ((line = fgetln(file, &length)) != NULL) {
+ /* Trim trailing whitespace (including optional newline). */
+ while (length > 0 && isspace(line[length - 1]))
+ length--;
+
+ /* Allocate a buffer to hold the line + terminating null. */
+ if ((buffer = malloc(length + 1)) == NULL)
+ err(EX_OSERR, NULL);
+ memcpy(buffer, line, length);
+ buffer[length] = '\0';
+
+ /* Strip out comments. */
+ if ((value = strchr(buffer, '#')) != NULL)
+ *value = '\0';
+
+ /* The name is first in the buffer. Trim whitespace.*/
+ name = buffer;
+ RTRIM(name);
+ while (isspace(*name))
+ name++;
+
+ /* Skip empty lines. */
+ if (strlen(name) == 0)
+ continue;
+
+ /* The name ends at the colon; the value starts there. */
+ if ((value = strrchr(buffer, ':')) == NULL)
+ ABORT_READ("no value associated with %s", name);
+ *value = '\0'; /* Null-terminate name. */
+ value++; /* Value starts afterwards. */
+
+ /* Trim leading and trailing whitespace. */
+ RTRIM(value);
+ while (isspace(*value))
+ value++;
+
+ /* Make sure there is a value left. */
+ if (strlen(value) == 0)
+ ABORT_READ("no value associated with %s", name);
+
+ /* Update our in-memory copy of the modepage entry value. */
+ if (editentry_set(name, value, 1) != 0) {
+ if (errno == ENOENT) {
+ /* No entry by the name. */
+ ABORT_READ("no such modepage entry \"%s\"",
+ name);
+ } else if (errno == EINVAL) {
+ /* Invalid value. */
+ ABORT_READ("Invalid value for entry \"%s\"",
+ name);
+ } else if (errno == ERANGE) {
+ /* Value out of range for entry type. */
+ ABORT_READ("value out of range for %s", name);
+ } else if (errno == EPERM) {
+ /* Entry is not editable; not fatal. */
+ warnx("modepage entry \"%s\" is read-only; "
+ "skipping.", name);
+ }
+ }
- arg = strtoul(line + strlen(name) + 2, 0, 0);
+ free(buffer);
}
+ return (ferror(file)? -1: 0);
- editind++;
- return arg;
+#undef ABORT_READ
}
static void
-edit_edit(void)
+modepage_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);
-}
+ char *editor;
+ char *commandline;
+ int fd;
+ int written;
-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 (!isatty(fileno(stdin))) {
+ /* Not a tty, read changes from stdin. */
+ modepage_read(stdin);
+ return;
}
- 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)");
+ /* Lookup editor to invoke. */
+ if ((editor = getenv("EDITOR")) == NULL)
+ editor = DEFAULT_EDITOR;
- verbose = 1;
+ /* Create temp file for editor to modify. */
+ if ((fd = mkstemp(edit_path)) == -1)
+ errx(EX_CANTCREAT, "mkstemp failed");
- mode_sense(device, page, 1, dbd, retry_count, timeout,
- data, sizeof(data));
+ atexit(cleanup_editfile);
- mh = (struct mode_header *)data;
- mph = (struct mode_page_header *)
- (((char *)mh) + sizeof(*mh) + mh->bdl);
+ if ((edit_file = fdopen(fd, "w")) == NULL)
+ err(EX_NOINPUT, "%s", edit_path);
- mode_pars = (char *)mph + sizeof(*mph);
+ written = modepage_write(edit_file, 1);
- 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);
+ fclose(edit_file);
+ edit_file = NULL;
- edit_rewind();
- buff_decode_visit(mode_pars, mh->mdl, fmt, edit_report, 0);
+ if (written == 0) {
+ warnx("no editable entries");
+ cleanup_editfile();
+ return;
+ }
- edit_edit();
+ /*
+ * Allocate memory to hold the command line (the 2 extra characters
+ * are to hold the argument separator (a space), and the terminating
+ * null character.
+ */
+ commandline = malloc(strlen(editor) + strlen(edit_path) + 2);
+ if (commandline == NULL)
+ err(EX_OSERR, NULL);
+ sprintf(commandline, "%s %s", editor, edit_path);
- edit_rewind();
- buff_encode_visit(mode_pars, mh->mdl, fmt, edit_get, 0);
+ /* Invoke the editor on the temp file. */
+ if (system(commandline) == -1)
+ err(EX_UNAVAILABLE, "could not invoke %s", editor);
+ free(commandline);
- /* Eliminate block descriptors:
- */
- bcopy((char *)mph, ((char *)mh) + sizeof(*mh),
- sizeof(*mph) + mph->page_length);
+ if ((edit_file = fopen(edit_path, "r")) == NULL)
+ err(EX_NOINPUT, "%s", edit_path);
- mh->bdl = mh->dev_spec_par = 0;
- mph = (struct mode_page_header *) (((char *)mh) + sizeof(*mh));
- mode_pars = ((char *)mph) + 2;
+ /* Read any changes made to the temp file. */
+ modepage_read(edit_file);
-#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
+ cleanup_editfile();
+}
- edit_done();
+static void
+modepage_dump(struct cam_device *device, int page, int page_control, int dbd,
+ int retries, int timeout)
+{
+ u_int8_t data[MAX_COMMAND_SIZE];/* Buffer to hold sense data. */
+ u_int8_t *mode_pars; /* Pointer to modepage params. */
+ struct scsi_mode_header_6 *mh; /* Location of mode header. */
+ struct scsi_mode_page_header *mph;
+ int index; /* Index for scanning mode params. */
+
+ mode_sense(device, page, page_control, dbd, retries, timeout, data,
+ sizeof(data));
+
+ mh = (struct scsi_mode_header_6 *)data;
+ mph = MODE_PAGE_HEADER(mh);
+ mode_pars = MODE_PAGE_DATA(mph);
+
+ /* Print the raw mode page data with newlines each 8 bytes. */
+ for (index = 0; index < mph->page_length; index++) {
+ printf("%02x%c",mode_pars[index],
+ (((index + 1) % 8) == 0) ? '\n' : ' ');
+ }
+ putchar('\n');
+}
- /* Make it permanent if pageselect is three.
- */
+static void
+cleanup_editfile(void)
+{
+ if (edit_file == NULL)
+ return;
+ if (fclose(edit_file) != 0 || unlink(edit_path) != 0)
+ warn("%s", edit_path);
+ edit_file = NULL;
+}
- mph->page_code &= ~0xC0; /* Clear PS and RESERVED */
- mh->mdl = 0; /* Reserved for mode select */
+void
+mode_edit(struct cam_device *device, int page, int page_control, int dbd,
+ int edit, int binary, int retry_count, int timeout)
+{
+ char *pagedb_path; /* Path to modepage database. */
+
+ if (edit && binary)
+ errx(EX_USAGE, "cannot edit in binary mode.");
+
+ if (! binary) {
+ if ((pagedb_path = getenv("SCSI_MODES")) == NULL)
+ pagedb_path = DEFAULT_SCSI_MODE_DB;
+
+ if (load_format(pagedb_path, page) != 0 && (edit || verbose)) {
+ if (errno == ENOENT) {
+ /* Modepage database file not found. */
+ warn("cannot open modepage database \"%s\"",
+ pagedb_path);
+ } else if (errno == ESRCH) {
+ /* Modepage entry not found in database. */
+ warnx("modepage %d not found in database"
+ "\"%s\"", page, pagedb_path);
+ }
+ /* We can recover in display mode, otherwise we exit. */
+ if (!edit) {
+ warnx("reverting to binary display only");
+ binary = 1;
+ } else
+ exit(EX_OSFILE);
+ }
- mode_select(device, (page_control == 3), retry_count,
- timeout, (u_int8_t *)mh, sizeof(*mh) + mh->bdl +
- sizeof(*mph) + mph->page_length);
+ editlist_populate(device, page, page_control, dbd, retry_count,
+ timeout);
+ }
- return;
+ if (edit) {
+ if (page_control != SMS_PAGE_CTRL_CURRENT &&
+ page_control != SMS_PAGE_CTRL_SAVED)
+ errx(EX_USAGE, "it only makes sense to edit page 0 "
+ "(current) or page 3 (saved values)");
+ modepage_edit();
+ editlist_save(device, page, page_control, dbd, retry_count,
+ timeout);
+ } else if (binary || STAILQ_EMPTY(&editlist)) {
+ /* Display without formatting information. */
+ modepage_dump(device, page, page_control, dbd, retry_count,
+ timeout);
+ } else {
+ /* Display with format. */
+ modepage_write(stdout, 0);
}
+}
- mode_sense(device, page, page_control, dbd, retry_count, timeout,
- data, sizeof(data));
+void
+mode_list(struct cam_device *device, int page_control, int dbd,
+ int retry_count, int timeout)
+{
+ u_int8_t data[MAX_COMMAND_SIZE];/* Buffer to hold sense data. */
+ u_int8_t *mode_pars; /* Pointer to modepage params. */
+ struct scsi_mode_header_6 *mh; /* Location of mode header. */
+ struct scsi_mode_page_header *mph;
+ struct pagename *nameentry;
+ char *pagedb_path;
+ int len;
+
+ if ((pagedb_path = getenv("SCSI_MODES")) == NULL)
+ pagedb_path = DEFAULT_SCSI_MODE_DB;
+
+ if (load_format(pagedb_path, 0) != 0 && verbose && errno == ENOENT) {
+ /* Modepage database file not found. */
+ warn("cannot open modepage database \"%s\"", pagedb_path);
+ }
- /* 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);
+ /* Build the list of all mode pages by querying the "all pages" page. */
+ mode_sense(device, SMS_ALL_PAGES_PAGE, page_control, dbd, retry_count,
+ timeout, data, sizeof(data));
+
+ mh = (struct scsi_mode_header_6 *)data;
+ len = mh->blk_desc_len; /* Skip block descriptors. */
+ /* Iterate through the pages in the reply. */
+ while (len < mh->data_length) {
+ /* Locate the next mode page header. */
+ mph = (struct scsi_mode_page_header *)
+ ((intptr_t)mh + sizeof(*mh) + len);
+ mode_pars = MODE_PAGE_DATA(mph);
+
+ mph->page_code &= SMS_PAGE_CODE;
+ nameentry = nameentry_lookup(mph->page_code);
+
+ if (nameentry == NULL || nameentry->name == NULL)
+ printf("0x%02x\n", mph->page_code);
+ else
+ printf("0x%02x\t%s\n", mph->page_code,
+ nameentry->name);
+ len += mph->page_length + sizeof(*mph);
}
}
diff --git a/sbin/camcontrol/util.c b/sbin/camcontrol/util.c
index 32f5c78..93f409b 100644
--- a/sbin/camcontrol/util.c
+++ b/sbin/camcontrol/util.c
@@ -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);
- }
-}
OpenPOWER on IntegriCloud