1,15c1,4
< /*
< * 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.
22c11,12
< * 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.
25,40c15,26
< * 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.
---
> * 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.
42,45c28
< /*
< * Taken from the original scsi(8) program.
< * from: scsi.c,v 1.17 1998/01/12 07:57:57 charnier Exp $";
< */
---
>
48c31
< "$FreeBSD: head/sbin/camcontrol/modeedit.c 56384 2000-01-21 23:19:30Z mjacob $";
---
> "$FreeBSD: head/sbin/camcontrol/modeedit.c 64382 2000-08-08 06:24:17Z kbyanc $";
50a34,37
> #include <sys/queue.h>
> #include <sys/types.h>
>
> #include <assert.h>
54d40
< #include <string.h>
55a42
> #include <string.h>
57,58c44
< #include <sys/file.h>
< #include <signal.h>
---
> #include <sysexits.h>
60a47
> #include <cam/scsi/scsi_all.h>
68,71c55,149
< /* iget: Integer argument callback
< */
< int
< iget(void *hook, char *name)
---
> #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)
>
>
> static void
> editentry_create(void *hook, int letter, void *arg, int count, char *name)
73,74c151
< struct get_hook *h = (struct get_hook *)hook;
< int arg;
---
> struct editentry *newentry; /* Buffer to hold new entry. */
76,83c153,156
< 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++;
---
> /* 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);
85,86c158,159
< if (verbose && name && *name)
< printf("%s: %d\n", name, arg);
---
> /* Trim any trailing whitespace for the entry name. */
> RTRIM(newentry->name);
88c161,166
< return arg;
---
> newentry->editable = (arg != NULL);
> newentry->type = letter;
> newentry->size = count; /* Placeholder; not accurate. */
> newentry->value.svalue = NULL;
>
> STAILQ_INSERT_TAIL(&editlist, newentry, link);
91,94c169,170
< /* cget: char * argument callback
< */
< char *
< cget(void *hook, char *name)
---
> static void
> editentry_update(void *hook, int letter, void *arg, int count, char *name)
96,97c172
< struct get_hook *h = (struct get_hook *)hook;
< char *arg;
---
> struct editentry *dest; /* Buffer to hold entry to update. */
99,103c174,192
< if (h->got >= h->argc)
< {
< fprintf(stderr, "Expecting a character pointer argument.\n");
< usage(0);
< exit(1);
---
> dest = editentry_lookup(name);
> assert(dest != NULL);
>
> 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;
>
> case 'c': /* Character array. */
> case 'z': /* Null-padded string. */
> editentry_set(name, (char *)arg, 0);
> break;
> default:
> /* NOTREACHED */
105,106c194
< arg = h->argv[h->got];
< h->got++;
---
> }
108,109c196,199
< if (verbose && name)
< printf("cget: %s: %s", name, arg);
---
> static int
> editentry_save(void *hook, char *name)
> {
> struct editentry *src; /* Entry value to save. */
111c201,220
< return arg;
---
> src = editentry_lookup(name);
> assert(src != NULL);
>
> switch (src->type) {
> case 'i': /* Byte-sized integral type. */
> case 'b': /* Bit-sized integral types. */
> case 't':
> return (src->value.ivalue);
> /* NOTREACHED */
>
> case 'c': /* Character array. */
> case 'z': /* Null-padded string. */
> return ((intptr_t)src->value.svalue);
> /* NOTREACHED */
>
> default:
> /* NOTREACHED */
> }
>
> return (0); /* This should never happen. */
114,117c223,224
< /* arg_put: "put argument" callback
< */
< void
< arg_put(void *hook, int letter, void *arg, int count, char *name)
---
> static struct editentry *
> editentry_lookup(char *name)
119,120c226
< if (verbose && name && *name)
< printf("%s: ", name);
---
> struct editentry *scan;
122,127c228
< switch(letter)
< {
< case 'i':
< case 'b':
< printf("%d ", (intptr_t)arg);
< break;
---
> assert(name != NULL);
129,132c230,233
< case 'c':
< case 'z':
< {
< char *p;
---
> STAILQ_FOREACH(scan, &editlist, link) {
> if (strcasecmp(scan->name, name) == 0)
> return (scan);
> }
134c235,237
< p = malloc(count + 1);
---
> /* Not found during list traversal. */
> return (NULL);
> }
136,147c239,246
< 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);
---
> 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. */
149c248,279
< free(p);
---
> /*
> * 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;
150a281,284
> if (dest->value.ivalue != ival)
> editlist_changed = 1;
> dest->value.ivalue = ival;
> break;
151a286,315
> 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;
154,155c318,319
< default:
< printf("Unknown format letter: '%c'\n", letter);
---
> default:
> /* NOTREACHED */
157,158c321,323
< if (verbose)
< putchar('\n');
---
>
> return (0);
> #undef RESOLUTION_MAX
161,163d325
< #define START_ENTRY '{'
< #define END_ENTRY '}'
<
165,167c327,328
< skipwhite(FILE *f)
< {
< int c;
---
> nameentry_create(int pagenum, char *name) {
> struct pagename *newentry;
169c330,331
< skip_again:
---
> if (pagenum < 0 || name == NULL || name[0] == '\0')
> return;
171,172c333,336
< while (isspace(c = getc(f)))
< ;
---
> /* 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);
174,177c338,351
< if (c == '#') {
< while ((c = getc(f)) != '\n' && c != EOF)
< ;
< goto skip_again;
---
> /* Trim any trailing whitespace for the page name. */
> RTRIM(newentry->name);
>
> newentry->pagenum = pagenum;
> SLIST_INSERT_HEAD(&namelist, newentry, link);
> }
>
> static struct pagename *
> nameentry_lookup(int pagenum) {
> struct pagename *scan;
>
> SLIST_FOREACH(scan, &namelist, link) {
> if (pagenum == scan->pagenum)
> return (scan);
180c354,355
< ungetc(c, f);
---
> /* Not found during list traversal. */
> return (NULL);
183,187c358,359
< /* 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 int
> load_format(char *pagedb_path, int page)
189,194c361,369
< 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");
---
> 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;
196,197c371,376
< if (new_db)
< mode_db = new_db;
---
> #define SETSTATE_LOCATE do { \
> str_pagenum[0] = '\0'; \
> str_pagename[0] = '\0'; \
> pagenum = -1; \
> state = LOCATE; \
> } while (0)
199,201c378,381
< modes = fopen(mode_db, "r");
< if (modes == 0)
< return 0;
---
> #define SETSTATE_PAGENAME do { \
> str_pagename[0] = '\0'; \
> state = PAGENAME; \
> } while (0)
203,204c383,386
< next = 0;
< found = 0;
---
> #define SETSTATE_PAGEDEF do { \
> format[0] = '\0'; \
> state = PAGEDEF; \
> } while (0)
206c388,391
< while (!found) {
---
> #define UPDATE_LINENO do { \
> if (c == '\n') \
> lineno++; \
> } while (0)
208c393
< skipwhite(modes);
---
> #define BUFFERFULL(buffer) (strlen(buffer) + 1 >= sizeof(buffer))
210,211c395,396
< if (fscanf(modes, "%i", &page_desc) != 1)
< break;
---
> if ((pagedb = fopen(pagedb_path, "r")) == NULL)
> returnerr(ENOENT);
213,214c398
< if (page_desc == page)
< found = 1;
---
> SLIST_INIT(&namelist);
216,218c400,404
< skipwhite(modes);
< if (getc(modes) != START_ENTRY)
< errx(1, "expected %c", START_ENTRY);
---
> depth = 0;
> lineno = 0;
> found = 0;
> SETSTATE_LOCATE;
> while ((c = fgetc(pagedb)) != EOF) {
220,224c406,429
< match = 1;
< while (match != 0) {
< c = getc(modes);
< if (c == EOF) {
< warnx("expected %c", END_ENTRY);
---
> /* 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;
> }
>
> /* 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");
225a431
> }
227,228c433,474
< if (c == START_ENTRY) {
< match++;
---
> 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");
230,233c476,490
< if (c == END_ENTRY) {
< match--;
< if (match == 0)
< break;
---
> break;
>
> 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");
235,237c492
< if (found && c != '\n') {
< if (next >= sizeof(fmt))
< errx(1, "buffer overflow");
---
> break;
239c494,507
< fmt[next++] = (u_char)c;
---
> 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");
240a509,512
> break;
>
> default:
> /* NOTREACHED */
241a514,515
>
> /* Repeat processing loop with next character. */
243d516
< fmt[next] = 0;
245,246c518,519
< return (found) ? fmt : 0;
< }
---
> if (ferror(pagedb))
> err(EX_OSFILE, "%s", pagedb_path);
248,254c521,522
< /* -------- edit: Mode Select Editor ---------
< */
< struct editinfo
< {
< int can_edit;
< int default_value;
< } editinfo[64]; /* XXX Bogus fixed size */
---
> /* Close the SCSI page database. */
> fclose(pagedb);
256,259c524,525
< static int editind;
< volatile int edit_opened;
< static FILE *edit_file;
< static char edit_name[L_tmpnam];
---
> if (!found) /* Never found a matching page. */
> returnerr(ESRCH);
261,264c527
< static inline void
< edit_rewind(void)
< {
< editind = 0;
---
> return (0);
268c531,532
< edit_done(void)
---
> editlist_populate(struct cam_device *device, int modepage, int page_control,
> int dbd, int retries, int timeout)
270c534,537
< int opened;
---
> 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;
272,273c539
< sigset_t all, prev;
< sigfillset(&all);
---
> STAILQ_INIT(&editlist);
275c541,543
< (void)sigprocmask(SIG_SETMASK, &all, &prev);
---
> /* Fetch changeable values; use to build initial editlist. */
> mode_sense(device, modepage, 1, dbd, retries, timeout, data,
> sizeof(data));
277,278c545,547
< opened = (int)edit_opened;
< edit_opened = 0;
---
> mh = (struct scsi_mode_header_6 *)data;
> mph = MODE_PAGE_HEADER(mh);
> mode_pars = MODE_PAGE_DATA(mph);
280c549,551
< (void)sigprocmask(SIG_SETMASK, &prev, 0);
---
> /* Decode the value data, creating edit_entries for each value. */
> buff_decode_visit(mode_pars, mh->data_length, format,
> editentry_create, 0);
282,288c553,557
< if (opened)
< {
< if (fclose(edit_file))
< warn("%s", edit_name);
< if (unlink(edit_name))
< warn("%s", edit_name);
< }
---
> /* 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);
292c561,562
< edit_init(void)
---
> editlist_save(struct cam_device *device, int modepage, int page_control,
> int dbd, int retries, int timeout)
294c564,567
< int fd;
---
> 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;
296,302c569,571
< 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;
---
> /* Make sure that something changed before continuing. */
> if (! editlist_changed)
> return;
304,305c573,580
< atexit(edit_done);
< }
---
> /*
> * 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));
307,311c582,585
< 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);
---
> /* Initial headers & offsets. */
> mh = (struct scsi_mode_header_6 *)data;
> mph = MODE_PAGE_HEADER(mh);
> mode_pars = MODE_PAGE_DATA(mph);
313,314c587,589
< if (editind >= sizeof(editinfo) / sizeof(editinfo[0]))
< errx(1, "edit table overflow");
---
> /* Encode the value data to be passed back to the device. */
> buff_encode_visit(mode_pars, mh->data_length, format,
> editentry_save, 0);
316,318c591,593
< editinfo[editind].can_edit = (arg != NULL);
< editind++;
< }
---
> /* Eliminate block descriptors. */
> bcopy(mph, ((u_int8_t *)mh) + sizeof(*mh),
> sizeof(*mph) + mph->page_length);
320,324c595,599
< 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);
---
> /* 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);
326,327c601,611
< editinfo[editind].default_value = (intptr_t)arg; /* truncated */
< editind++;
---
> 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);
330,331c614,615
< static void
< edit_report(void *hook, int letter, void *arg, int count, char *name)
---
> static int
> modepage_write(FILE *file, int editonly)
333,335c617,618
< if (editinfo[editind].can_edit) {
< if (letter != 'i' && letter != 'b')
< errx(1, "can't report format %c", letter);
---
> struct editentry *scan;
> int written = 0;
337c620,630
< fprintf(edit_file, "%s: %d\n", name, (intptr_t)arg);
---
> 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);
> }
> }
339,340c632
<
< editind++;
---
> return (written);
344c636
< edit_get(void *hook, char *name)
---
> modepage_read(FILE *file)
346c638,642
< int arg = editinfo[editind].default_value;
---
> 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. */
348,351c644,648
< if (editinfo[editind].can_edit) {
< char line[80];
< if (fgets(line, sizeof(line), edit_file) == 0)
< err(1, "fgets");
---
> #define ABORT_READ(message, param) do { \
> warnx(message, param); \
> free(buffer); \
> returnerr(EAGAIN); \
> } while (0)
353c650,653
< line[strlen(line) - 1] = 0;
---
> while ((line = fgetln(file, &length)) != NULL) {
> /* Trim trailing whitespace (including optional newline). */
> while (length > 0 && isspace(line[length - 1]))
> length--;
355,356c655,659
< if (strncmp(name, line, strlen(name)) != 0)
< errx(1, "expected \"%s\" and read \"%s\"", name, line);
---
> /* 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';
358c661,710
< arg = strtoul(line + strlen(name) + 2, 0, 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);
> }
> }
>
> free(buffer);
359a712
> return (ferror(file)? -1: 0);
361,362c714
< editind++;
< return arg;
---
> #undef ABORT_READ
366c718
< edit_edit(void)
---
> modepage_edit(void)
368,371c720,723
< char *system_line;
< char *editor = getenv("EDITOR");
< if (!editor)
< editor = "vi";
---
> char *editor;
> char *commandline;
> int fd;
> int written;
373c725,729
< fclose(edit_file);
---
> if (!isatty(fileno(stdin))) {
> /* Not a tty, read changes from stdin. */
> modepage_read(stdin);
> return;
> }
375,378c731,733
< system_line = malloc(strlen(editor) + strlen(edit_name) + 6);
< sprintf(system_line, "%s %s", editor, edit_name);
< system(system_line);
< free(system_line);
---
> /* Lookup editor to invoke. */
> if ((editor = getenv("EDITOR")) == NULL)
> editor = DEFAULT_EDITOR;
380,382c735,737
< if ((edit_file = fopen(edit_name, "r")) == 0)
< err(1, "%s", edit_name);
< }
---
> /* Create temp file for editor to modify. */
> if ((fd = mkstemp(edit_path)) == -1)
> errx(EX_CANTCREAT, "mkstemp failed");
384,397c739
< 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 */
< };
---
> atexit(cleanup_editfile);
399,403c741,742
< struct mode_page_header
< {
< u_char page_code;
< u_char page_length;
< };
---
> if ((edit_file = fdopen(fd, "w")) == NULL)
> err(EX_NOINPUT, "%s", edit_path);
405,406c744
< struct mode_header *mh;
< struct mode_page_header *mph;
---
> written = modepage_write(edit_file, 1);
408,413c746,752
< 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"));
---
> fclose(edit_file);
> edit_file = NULL;
>
> if (written == 0) {
> warnx("no editable entries");
> cleanup_editfile();
> return;
416,418c755,763
< if (edit) {
< if (!fmt)
< errx(1, "can't edit without a format");
---
> /*
> * 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);
420,422c765,768
< if (page_control != 0 && page_control != 3)
< errx(1, "it only makes sense to edit page 0 "
< "(current) or page 3 (saved values)");
---
> /* Invoke the editor on the temp file. */
> if (system(commandline) == -1)
> err(EX_UNAVAILABLE, "could not invoke %s", editor);
> free(commandline);
424c770,771
< verbose = 1;
---
> if ((edit_file = fopen(edit_path, "r")) == NULL)
> err(EX_NOINPUT, "%s", edit_path);
426,427c773,774
< mode_sense(device, page, 1, dbd, retry_count, timeout,
< data, sizeof(data));
---
> /* Read any changes made to the temp file. */
> modepage_read(edit_file);
429,431c776,777
< mh = (struct mode_header *)data;
< mph = (struct mode_page_header *)
< (((char *)mh) + sizeof(*mh) + mh->bdl);
---
> cleanup_editfile();
> }
433c779,787
< mode_pars = (char *)mph + sizeof(*mph);
---
> 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. */
435,436c789,790
< edit_init();
< buff_decode_visit(mode_pars, mh->mdl, fmt, edit_check, 0);
---
> mode_sense(device, page, page_control, dbd, retries, timeout, data,
> sizeof(data));
438,439c792,794
< mode_sense(device, page, 0, dbd, retry_count, timeout,
< data, sizeof(data));
---
> mh = (struct scsi_mode_header_6 *)data;
> mph = MODE_PAGE_HEADER(mh);
> mode_pars = MODE_PAGE_DATA(mph);
441,442c796,802
< edit_rewind();
< buff_decode_visit(mode_pars, mh->mdl, fmt, edit_defaults, 0);
---
> /* 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');
> }
444,445c804,812
< edit_rewind();
< buff_decode_visit(mode_pars, mh->mdl, fmt, edit_report, 0);
---
> 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;
> }
447c814,818
< edit_edit();
---
> 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. */
449,450c820,821
< edit_rewind();
< buff_encode_visit(mode_pars, mh->mdl, fmt, edit_get, 0);
---
> if (edit && binary)
> errx(EX_USAGE, "cannot edit in binary mode.");
452,455c823,825
< /* Eliminate block descriptors:
< */
< bcopy((char *)mph, ((char *)mh) + sizeof(*mh),
< sizeof(*mph) + mph->page_length);
---
> if (! binary) {
> if ((pagedb_path = getenv("SCSI_MODES")) == NULL)
> pagedb_path = DEFAULT_SCSI_MODE_DB;
457,459c827,843
< mh->bdl = mh->dev_spec_par = 0;
< mph = (struct mode_page_header *) (((char *)mh) + sizeof(*mh));
< mode_pars = ((char *)mph) + 2;
---
> 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);
> }
461,467c845,847
< #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
---
> editlist_populate(device, page, page_control, dbd, retry_count,
> timeout);
> }
469c849,865
< edit_done();
---
> 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);
> }
> }
471,472c867,877
< /* Make it permanent if pageselect is three.
< */
---
> 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;
474,475c879,880
< mph->page_code &= ~0xC0; /* Clear PS and RESERVED */
< mh->mdl = 0; /* Reserved for mode select */
---
> if ((pagedb_path = getenv("SCSI_MODES")) == NULL)
> pagedb_path = DEFAULT_SCSI_MODE_DB;
477,481c882,884
< mode_select(device, (page_control == 3), retry_count,
< timeout, (u_int8_t *)mh, sizeof(*mh) + mh->bdl +
< sizeof(*mph) + mph->page_length);
<
< return;
---
> if (load_format(pagedb_path, 0) != 0 && verbose && errno == ENOENT) {
> /* Modepage database file not found. */
> warn("cannot open modepage database \"%s\"", pagedb_path);
484,485c887,889
< mode_sense(device, page, page_control, dbd, retry_count, timeout,
< data, sizeof(data));
---
> /* 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));
487,491c891,898
< /* 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);
---
> 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);
493,501c900,908
< 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);
---
> 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);
< /*
< * 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.
22c11,12
< * 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.
25,40c15,26
< * 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.
---
> * 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.
42,45c28
< /*
< * Taken from the original scsi(8) program.
< * from: scsi.c,v 1.17 1998/01/12 07:57:57 charnier Exp $";
< */
---
>
48c31
< "$FreeBSD: head/sbin/camcontrol/modeedit.c 56384 2000-01-21 23:19:30Z mjacob $";
---
> "$FreeBSD: head/sbin/camcontrol/modeedit.c 64382 2000-08-08 06:24:17Z kbyanc $";
50a34,37
> #include <sys/queue.h>
> #include <sys/types.h>
>
> #include <assert.h>
54d40
< #include <string.h>
55a42
> #include <string.h>
57,58c44
< #include <sys/file.h>
< #include <signal.h>
---
> #include <sysexits.h>
60a47
> #include <cam/scsi/scsi_all.h>
68,71c55,149
< /* iget: Integer argument callback
< */
< int
< iget(void *hook, char *name)
---
> #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)
>
>
> static void
> editentry_create(void *hook, int letter, void *arg, int count, char *name)
73,74c151
< struct get_hook *h = (struct get_hook *)hook;
< int arg;
---
> struct editentry *newentry; /* Buffer to hold new entry. */
76,83c153,156
< 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++;
---
> /* 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);
85,86c158,159
< if (verbose && name && *name)
< printf("%s: %d\n", name, arg);
---
> /* Trim any trailing whitespace for the entry name. */
> RTRIM(newentry->name);
88c161,166
< return arg;
---
> newentry->editable = (arg != NULL);
> newentry->type = letter;
> newentry->size = count; /* Placeholder; not accurate. */
> newentry->value.svalue = NULL;
>
> STAILQ_INSERT_TAIL(&editlist, newentry, link);
91,94c169,170
< /* cget: char * argument callback
< */
< char *
< cget(void *hook, char *name)
---
> static void
> editentry_update(void *hook, int letter, void *arg, int count, char *name)
96,97c172
< struct get_hook *h = (struct get_hook *)hook;
< char *arg;
---
> struct editentry *dest; /* Buffer to hold entry to update. */
99,103c174,192
< if (h->got >= h->argc)
< {
< fprintf(stderr, "Expecting a character pointer argument.\n");
< usage(0);
< exit(1);
---
> dest = editentry_lookup(name);
> assert(dest != NULL);
>
> 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;
>
> case 'c': /* Character array. */
> case 'z': /* Null-padded string. */
> editentry_set(name, (char *)arg, 0);
> break;
> default:
> /* NOTREACHED */
105,106c194
< arg = h->argv[h->got];
< h->got++;
---
> }
108,109c196,199
< if (verbose && name)
< printf("cget: %s: %s", name, arg);
---
> static int
> editentry_save(void *hook, char *name)
> {
> struct editentry *src; /* Entry value to save. */
111c201,220
< return arg;
---
> src = editentry_lookup(name);
> assert(src != NULL);
>
> switch (src->type) {
> case 'i': /* Byte-sized integral type. */
> case 'b': /* Bit-sized integral types. */
> case 't':
> return (src->value.ivalue);
> /* NOTREACHED */
>
> case 'c': /* Character array. */
> case 'z': /* Null-padded string. */
> return ((intptr_t)src->value.svalue);
> /* NOTREACHED */
>
> default:
> /* NOTREACHED */
> }
>
> return (0); /* This should never happen. */
114,117c223,224
< /* arg_put: "put argument" callback
< */
< void
< arg_put(void *hook, int letter, void *arg, int count, char *name)
---
> static struct editentry *
> editentry_lookup(char *name)
119,120c226
< if (verbose && name && *name)
< printf("%s: ", name);
---
> struct editentry *scan;
122,127c228
< switch(letter)
< {
< case 'i':
< case 'b':
< printf("%d ", (intptr_t)arg);
< break;
---
> assert(name != NULL);
129,132c230,233
< case 'c':
< case 'z':
< {
< char *p;
---
> STAILQ_FOREACH(scan, &editlist, link) {
> if (strcasecmp(scan->name, name) == 0)
> return (scan);
> }
134c235,237
< p = malloc(count + 1);
---
> /* Not found during list traversal. */
> return (NULL);
> }
136,147c239,246
< 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);
---
> 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. */
149c248,279
< free(p);
---
> /*
> * 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;
150a281,284
> if (dest->value.ivalue != ival)
> editlist_changed = 1;
> dest->value.ivalue = ival;
> break;
151a286,315
> 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;
154,155c318,319
< default:
< printf("Unknown format letter: '%c'\n", letter);
---
> default:
> /* NOTREACHED */
157,158c321,323
< if (verbose)
< putchar('\n');
---
>
> return (0);
> #undef RESOLUTION_MAX
161,163d325
< #define START_ENTRY '{'
< #define END_ENTRY '}'
<
165,167c327,328
< skipwhite(FILE *f)
< {
< int c;
---
> nameentry_create(int pagenum, char *name) {
> struct pagename *newentry;
169c330,331
< skip_again:
---
> if (pagenum < 0 || name == NULL || name[0] == '\0')
> return;
171,172c333,336
< while (isspace(c = getc(f)))
< ;
---
> /* 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);
174,177c338,351
< if (c == '#') {
< while ((c = getc(f)) != '\n' && c != EOF)
< ;
< goto skip_again;
---
> /* Trim any trailing whitespace for the page name. */
> RTRIM(newentry->name);
>
> newentry->pagenum = pagenum;
> SLIST_INSERT_HEAD(&namelist, newentry, link);
> }
>
> static struct pagename *
> nameentry_lookup(int pagenum) {
> struct pagename *scan;
>
> SLIST_FOREACH(scan, &namelist, link) {
> if (pagenum == scan->pagenum)
> return (scan);
180c354,355
< ungetc(c, f);
---
> /* Not found during list traversal. */
> return (NULL);
183,187c358,359
< /* 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 int
> load_format(char *pagedb_path, int page)
189,194c361,369
< 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");
---
> 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;
196,197c371,376
< if (new_db)
< mode_db = new_db;
---
> #define SETSTATE_LOCATE do { \
> str_pagenum[0] = '\0'; \
> str_pagename[0] = '\0'; \
> pagenum = -1; \
> state = LOCATE; \
> } while (0)
199,201c378,381
< modes = fopen(mode_db, "r");
< if (modes == 0)
< return 0;
---
> #define SETSTATE_PAGENAME do { \
> str_pagename[0] = '\0'; \
> state = PAGENAME; \
> } while (0)
203,204c383,386
< next = 0;
< found = 0;
---
> #define SETSTATE_PAGEDEF do { \
> format[0] = '\0'; \
> state = PAGEDEF; \
> } while (0)
206c388,391
< while (!found) {
---
> #define UPDATE_LINENO do { \
> if (c == '\n') \
> lineno++; \
> } while (0)
208c393
< skipwhite(modes);
---
> #define BUFFERFULL(buffer) (strlen(buffer) + 1 >= sizeof(buffer))
210,211c395,396
< if (fscanf(modes, "%i", &page_desc) != 1)
< break;
---
> if ((pagedb = fopen(pagedb_path, "r")) == NULL)
> returnerr(ENOENT);
213,214c398
< if (page_desc == page)
< found = 1;
---
> SLIST_INIT(&namelist);
216,218c400,404
< skipwhite(modes);
< if (getc(modes) != START_ENTRY)
< errx(1, "expected %c", START_ENTRY);
---
> depth = 0;
> lineno = 0;
> found = 0;
> SETSTATE_LOCATE;
> while ((c = fgetc(pagedb)) != EOF) {
220,224c406,429
< match = 1;
< while (match != 0) {
< c = getc(modes);
< if (c == EOF) {
< warnx("expected %c", END_ENTRY);
---
> /* 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;
> }
>
> /* 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");
225a431
> }
227,228c433,474
< if (c == START_ENTRY) {
< match++;
---
> 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");
230,233c476,490
< if (c == END_ENTRY) {
< match--;
< if (match == 0)
< break;
---
> break;
>
> 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");
235,237c492
< if (found && c != '\n') {
< if (next >= sizeof(fmt))
< errx(1, "buffer overflow");
---
> break;
239c494,507
< fmt[next++] = (u_char)c;
---
> 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");
240a509,512
> break;
>
> default:
> /* NOTREACHED */
241a514,515
>
> /* Repeat processing loop with next character. */
243d516
< fmt[next] = 0;
245,246c518,519
< return (found) ? fmt : 0;
< }
---
> if (ferror(pagedb))
> err(EX_OSFILE, "%s", pagedb_path);
248,254c521,522
< /* -------- edit: Mode Select Editor ---------
< */
< struct editinfo
< {
< int can_edit;
< int default_value;
< } editinfo[64]; /* XXX Bogus fixed size */
---
> /* Close the SCSI page database. */
> fclose(pagedb);
256,259c524,525
< static int editind;
< volatile int edit_opened;
< static FILE *edit_file;
< static char edit_name[L_tmpnam];
---
> if (!found) /* Never found a matching page. */
> returnerr(ESRCH);
261,264c527
< static inline void
< edit_rewind(void)
< {
< editind = 0;
---
> return (0);
268c531,532
< edit_done(void)
---
> editlist_populate(struct cam_device *device, int modepage, int page_control,
> int dbd, int retries, int timeout)
270c534,537
< int opened;
---
> 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;
272,273c539
< sigset_t all, prev;
< sigfillset(&all);
---
> STAILQ_INIT(&editlist);
275c541,543
< (void)sigprocmask(SIG_SETMASK, &all, &prev);
---
> /* Fetch changeable values; use to build initial editlist. */
> mode_sense(device, modepage, 1, dbd, retries, timeout, data,
> sizeof(data));
277,278c545,547
< opened = (int)edit_opened;
< edit_opened = 0;
---
> mh = (struct scsi_mode_header_6 *)data;
> mph = MODE_PAGE_HEADER(mh);
> mode_pars = MODE_PAGE_DATA(mph);
280c549,551
< (void)sigprocmask(SIG_SETMASK, &prev, 0);
---
> /* Decode the value data, creating edit_entries for each value. */
> buff_decode_visit(mode_pars, mh->data_length, format,
> editentry_create, 0);
282,288c553,557
< if (opened)
< {
< if (fclose(edit_file))
< warn("%s", edit_name);
< if (unlink(edit_name))
< warn("%s", edit_name);
< }
---
> /* 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);
292c561,562
< edit_init(void)
---
> editlist_save(struct cam_device *device, int modepage, int page_control,
> int dbd, int retries, int timeout)
294c564,567
< int fd;
---
> 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;
296,302c569,571
< 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;
---
> /* Make sure that something changed before continuing. */
> if (! editlist_changed)
> return;
304,305c573,580
< atexit(edit_done);
< }
---
> /*
> * 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));
307,311c582,585
< 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);
---
> /* Initial headers & offsets. */
> mh = (struct scsi_mode_header_6 *)data;
> mph = MODE_PAGE_HEADER(mh);
> mode_pars = MODE_PAGE_DATA(mph);
313,314c587,589
< if (editind >= sizeof(editinfo) / sizeof(editinfo[0]))
< errx(1, "edit table overflow");
---
> /* Encode the value data to be passed back to the device. */
> buff_encode_visit(mode_pars, mh->data_length, format,
> editentry_save, 0);
316,318c591,593
< editinfo[editind].can_edit = (arg != NULL);
< editind++;
< }
---
> /* Eliminate block descriptors. */
> bcopy(mph, ((u_int8_t *)mh) + sizeof(*mh),
> sizeof(*mph) + mph->page_length);
320,324c595,599
< 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);
---
> /* 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);
326,327c601,611
< editinfo[editind].default_value = (intptr_t)arg; /* truncated */
< editind++;
---
> 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);
330,331c614,615
< static void
< edit_report(void *hook, int letter, void *arg, int count, char *name)
---
> static int
> modepage_write(FILE *file, int editonly)
333,335c617,618
< if (editinfo[editind].can_edit) {
< if (letter != 'i' && letter != 'b')
< errx(1, "can't report format %c", letter);
---
> struct editentry *scan;
> int written = 0;
337c620,630
< fprintf(edit_file, "%s: %d\n", name, (intptr_t)arg);
---
> 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);
> }
> }
339,340c632
<
< editind++;
---
> return (written);
344c636
< edit_get(void *hook, char *name)
---
> modepage_read(FILE *file)
346c638,642
< int arg = editinfo[editind].default_value;
---
> 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. */
348,351c644,648
< if (editinfo[editind].can_edit) {
< char line[80];
< if (fgets(line, sizeof(line), edit_file) == 0)
< err(1, "fgets");
---
> #define ABORT_READ(message, param) do { \
> warnx(message, param); \
> free(buffer); \
> returnerr(EAGAIN); \
> } while (0)
353c650,653
< line[strlen(line) - 1] = 0;
---
> while ((line = fgetln(file, &length)) != NULL) {
> /* Trim trailing whitespace (including optional newline). */
> while (length > 0 && isspace(line[length - 1]))
> length--;
355,356c655,659
< if (strncmp(name, line, strlen(name)) != 0)
< errx(1, "expected \"%s\" and read \"%s\"", name, line);
---
> /* 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';
358c661,710
< arg = strtoul(line + strlen(name) + 2, 0, 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);
> }
> }
>
> free(buffer);
359a712
> return (ferror(file)? -1: 0);
361,362c714
< editind++;
< return arg;
---
> #undef ABORT_READ
366c718
< edit_edit(void)
---
> modepage_edit(void)
368,371c720,723
< char *system_line;
< char *editor = getenv("EDITOR");
< if (!editor)
< editor = "vi";
---
> char *editor;
> char *commandline;
> int fd;
> int written;
373c725,729
< fclose(edit_file);
---
> if (!isatty(fileno(stdin))) {
> /* Not a tty, read changes from stdin. */
> modepage_read(stdin);
> return;
> }
375,378c731,733
< system_line = malloc(strlen(editor) + strlen(edit_name) + 6);
< sprintf(system_line, "%s %s", editor, edit_name);
< system(system_line);
< free(system_line);
---
> /* Lookup editor to invoke. */
> if ((editor = getenv("EDITOR")) == NULL)
> editor = DEFAULT_EDITOR;
380,382c735,737
< if ((edit_file = fopen(edit_name, "r")) == 0)
< err(1, "%s", edit_name);
< }
---
> /* Create temp file for editor to modify. */
> if ((fd = mkstemp(edit_path)) == -1)
> errx(EX_CANTCREAT, "mkstemp failed");
384,397c739
< 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 */
< };
---
> atexit(cleanup_editfile);
399,403c741,742
< struct mode_page_header
< {
< u_char page_code;
< u_char page_length;
< };
---
> if ((edit_file = fdopen(fd, "w")) == NULL)
> err(EX_NOINPUT, "%s", edit_path);
405,406c744
< struct mode_header *mh;
< struct mode_page_header *mph;
---
> written = modepage_write(edit_file, 1);
408,413c746,752
< 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"));
---
> fclose(edit_file);
> edit_file = NULL;
>
> if (written == 0) {
> warnx("no editable entries");
> cleanup_editfile();
> return;
416,418c755,763
< if (edit) {
< if (!fmt)
< errx(1, "can't edit without a format");
---
> /*
> * 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);
420,422c765,768
< if (page_control != 0 && page_control != 3)
< errx(1, "it only makes sense to edit page 0 "
< "(current) or page 3 (saved values)");
---
> /* Invoke the editor on the temp file. */
> if (system(commandline) == -1)
> err(EX_UNAVAILABLE, "could not invoke %s", editor);
> free(commandline);
424c770,771
< verbose = 1;
---
> if ((edit_file = fopen(edit_path, "r")) == NULL)
> err(EX_NOINPUT, "%s", edit_path);
426,427c773,774
< mode_sense(device, page, 1, dbd, retry_count, timeout,
< data, sizeof(data));
---
> /* Read any changes made to the temp file. */
> modepage_read(edit_file);
429,431c776,777
< mh = (struct mode_header *)data;
< mph = (struct mode_page_header *)
< (((char *)mh) + sizeof(*mh) + mh->bdl);
---
> cleanup_editfile();
> }
433c779,787
< mode_pars = (char *)mph + sizeof(*mph);
---
> 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. */
435,436c789,790
< edit_init();
< buff_decode_visit(mode_pars, mh->mdl, fmt, edit_check, 0);
---
> mode_sense(device, page, page_control, dbd, retries, timeout, data,
> sizeof(data));
438,439c792,794
< mode_sense(device, page, 0, dbd, retry_count, timeout,
< data, sizeof(data));
---
> mh = (struct scsi_mode_header_6 *)data;
> mph = MODE_PAGE_HEADER(mh);
> mode_pars = MODE_PAGE_DATA(mph);
441,442c796,802
< edit_rewind();
< buff_decode_visit(mode_pars, mh->mdl, fmt, edit_defaults, 0);
---
> /* 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');
> }
444,445c804,812
< edit_rewind();
< buff_decode_visit(mode_pars, mh->mdl, fmt, edit_report, 0);
---
> 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;
> }
447c814,818
< edit_edit();
---
> 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. */
449,450c820,821
< edit_rewind();
< buff_encode_visit(mode_pars, mh->mdl, fmt, edit_get, 0);
---
> if (edit && binary)
> errx(EX_USAGE, "cannot edit in binary mode.");
452,455c823,825
< /* Eliminate block descriptors:
< */
< bcopy((char *)mph, ((char *)mh) + sizeof(*mh),
< sizeof(*mph) + mph->page_length);
---
> if (! binary) {
> if ((pagedb_path = getenv("SCSI_MODES")) == NULL)
> pagedb_path = DEFAULT_SCSI_MODE_DB;
457,459c827,843
< mh->bdl = mh->dev_spec_par = 0;
< mph = (struct mode_page_header *) (((char *)mh) + sizeof(*mh));
< mode_pars = ((char *)mph) + 2;
---
> 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);
> }
461,467c845,847
< #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
---
> editlist_populate(device, page, page_control, dbd, retry_count,
> timeout);
> }
469c849,865
< edit_done();
---
> 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);
> }
> }
471,472c867,877
< /* Make it permanent if pageselect is three.
< */
---
> 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;
474,475c879,880
< mph->page_code &= ~0xC0; /* Clear PS and RESERVED */
< mh->mdl = 0; /* Reserved for mode select */
---
> if ((pagedb_path = getenv("SCSI_MODES")) == NULL)
> pagedb_path = DEFAULT_SCSI_MODE_DB;
477,481c882,884
< mode_select(device, (page_control == 3), retry_count,
< timeout, (u_int8_t *)mh, sizeof(*mh) + mh->bdl +
< sizeof(*mph) + mph->page_length);
<
< return;
---
> if (load_format(pagedb_path, 0) != 0 && verbose && errno == ENOENT) {
> /* Modepage database file not found. */
> warn("cannot open modepage database \"%s\"", pagedb_path);
484,485c887,889
< mode_sense(device, page, page_control, dbd, retry_count, timeout,
< data, sizeof(data));
---
> /* 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));
487,491c891,898
< /* 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);
---
> 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);
493,501c900,908
< 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);
---
> 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);