diff options
author | kbyanc <kbyanc@FreeBSD.org> | 2000-08-08 06:24:17 +0000 |
---|---|---|
committer | kbyanc <kbyanc@FreeBSD.org> | 2000-08-08 06:24:17 +0000 |
commit | 8f0afc6b0aa752cb23dfcf91f2285bf6c825405d (patch) | |
tree | 034cb732a278c59e0304f2f93947626f19ea5385 /sbin/camcontrol | |
parent | 18a0d975369707cba8c9deeb3de4db05dafb9487 (diff) | |
download | FreeBSD-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/camcontrol')
-rw-r--r-- | sbin/camcontrol/Makefile | 2 | ||||
-rw-r--r-- | sbin/camcontrol/camcontrol.8 | 20 | ||||
-rw-r--r-- | sbin/camcontrol/camcontrol.c | 26 | ||||
-rw-r--r-- | sbin/camcontrol/camcontrol.h | 4 | ||||
-rw-r--r-- | sbin/camcontrol/modeedit.c | 1153 | ||||
-rw-r--r-- | sbin/camcontrol/util.c | 357 |
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); - } -} |