17e32b20dSKelly Yancey /*- 27e32b20dSKelly Yancey * Copyright (c) 2000 Kelly Yancey <kbyanc@posi.net> 37e32b20dSKelly Yancey * Derived from work done by Julian Elischer <julian@tfs.com, 47e32b20dSKelly Yancey * julian@dialix.oz.au>, 1993, and Peter Dufault <dufault@hda.com>, 1994. 5525689f1SJustin T. Gibbs * All rights reserved. 6525689f1SJustin T. Gibbs * 7525689f1SJustin T. Gibbs * Redistribution and use in source and binary forms, with or without 8525689f1SJustin T. Gibbs * modification, are permitted provided that the following conditions 9525689f1SJustin T. Gibbs * are met: 10525689f1SJustin T. Gibbs * 1. Redistributions of source code must retain the above copyright 117e32b20dSKelly Yancey * notice, this list of conditions and the following disclaimer, 127e32b20dSKelly Yancey * without modification, immediately at the beginning of the file. 13525689f1SJustin T. Gibbs * 2. Redistributions in binary form must reproduce the above copyright 14525689f1SJustin T. Gibbs * notice, this list of conditions and the following disclaimer in the 15525689f1SJustin T. Gibbs * documentation and/or other materials provided with the distribution. 16525689f1SJustin T. Gibbs * 177e32b20dSKelly Yancey * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 187e32b20dSKelly Yancey * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 197e32b20dSKelly Yancey * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 207e32b20dSKelly Yancey * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 217e32b20dSKelly Yancey * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 227e32b20dSKelly Yancey * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 237e32b20dSKelly Yancey * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 247e32b20dSKelly Yancey * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 257e32b20dSKelly Yancey * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 267e32b20dSKelly Yancey * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27525689f1SJustin T. Gibbs */ 287e32b20dSKelly Yancey 29bf2783a0SDavid E. O'Brien #include <sys/cdefs.h> 30bf2783a0SDavid E. O'Brien __FBSDID("$FreeBSD$"); 31525689f1SJustin T. Gibbs 327e32b20dSKelly Yancey #include <sys/queue.h> 337e32b20dSKelly Yancey #include <sys/types.h> 347e32b20dSKelly Yancey 357e32b20dSKelly Yancey #include <assert.h> 36525689f1SJustin T. Gibbs #include <ctype.h> 37525689f1SJustin T. Gibbs #include <err.h> 38525689f1SJustin T. Gibbs #include <errno.h> 39525689f1SJustin T. Gibbs #include <stdlib.h> 407e32b20dSKelly Yancey #include <string.h> 41525689f1SJustin T. Gibbs #include <stdio.h> 427e32b20dSKelly Yancey #include <sysexits.h> 43525689f1SJustin T. Gibbs #include <unistd.h> 44525689f1SJustin T. Gibbs 457e32b20dSKelly Yancey #include <cam/scsi/scsi_all.h> 46525689f1SJustin T. Gibbs #include <cam/cam.h> 47525689f1SJustin T. Gibbs #include <cam/cam_ccb.h> 48525689f1SJustin T. Gibbs #include <camlib.h> 49525689f1SJustin T. Gibbs #include "camcontrol.h" 50525689f1SJustin T. Gibbs 51525689f1SJustin T. Gibbs int verbose = 0; 52525689f1SJustin T. Gibbs 537e32b20dSKelly Yancey #define DEFAULT_SCSI_MODE_DB "/usr/share/misc/scsi_modes" 547e32b20dSKelly Yancey #define DEFAULT_EDITOR "vi" 557e32b20dSKelly Yancey #define MAX_FORMAT_SPEC 4096 /* Max CDB format specifier. */ 567e32b20dSKelly Yancey #define MAX_PAGENUM_LEN 10 /* Max characters in page num. */ 577e32b20dSKelly Yancey #define MAX_PAGENAME_LEN 64 /* Max characters in page name. */ 587e32b20dSKelly Yancey #define PAGEDEF_START '{' /* Page definition delimiter. */ 597e32b20dSKelly Yancey #define PAGEDEF_END '}' /* Page definition delimiter. */ 607e32b20dSKelly Yancey #define PAGENAME_START '"' /* Page name delimiter. */ 617e32b20dSKelly Yancey #define PAGENAME_END '"' /* Page name delimiter. */ 627e32b20dSKelly Yancey #define PAGEENTRY_END ';' /* Page entry terminator (optional). */ 637e32b20dSKelly Yancey #define MAX_COMMAND_SIZE 255 /* Mode/Log sense data buffer size. */ 64354a0adaSKelly Yancey #define PAGE_CTRL_SHIFT 6 /* Bit offset to page control field. */ 657e32b20dSKelly Yancey 667e32b20dSKelly Yancey 677e32b20dSKelly Yancey /* Macros for working with mode pages. */ 687e32b20dSKelly Yancey #define MODE_PAGE_HEADER(mh) \ 697e32b20dSKelly Yancey (struct scsi_mode_page_header *)find_mode_page_6(mh) 707e32b20dSKelly Yancey 717e32b20dSKelly Yancey #define MODE_PAGE_DATA(mph) \ 727e32b20dSKelly Yancey (u_int8_t *)(mph) + sizeof(struct scsi_mode_page_header) 737e32b20dSKelly Yancey 747e32b20dSKelly Yancey 757e32b20dSKelly Yancey struct editentry { 767e32b20dSKelly Yancey STAILQ_ENTRY(editentry) link; 777e32b20dSKelly Yancey char *name; 787e32b20dSKelly Yancey char type; 797e32b20dSKelly Yancey int editable; 807e32b20dSKelly Yancey int size; 817e32b20dSKelly Yancey union { 827e32b20dSKelly Yancey int ivalue; 837e32b20dSKelly Yancey char *svalue; 847e32b20dSKelly Yancey } value; 857e32b20dSKelly Yancey }; 867e32b20dSKelly Yancey STAILQ_HEAD(, editentry) editlist; /* List of page entries. */ 877e32b20dSKelly Yancey int editlist_changed = 0; /* Whether any entries were changed. */ 887e32b20dSKelly Yancey 897e32b20dSKelly Yancey struct pagename { 907e32b20dSKelly Yancey SLIST_ENTRY(pagename) link; 917e32b20dSKelly Yancey int pagenum; 927e32b20dSKelly Yancey char *name; 937e32b20dSKelly Yancey }; 947e32b20dSKelly Yancey SLIST_HEAD(, pagename) namelist; /* Page number to name mappings. */ 957e32b20dSKelly Yancey 967e32b20dSKelly Yancey static char format[MAX_FORMAT_SPEC]; /* Buffer for scsi cdb format def. */ 977e32b20dSKelly Yancey 987e32b20dSKelly Yancey static FILE *edit_file = NULL; /* File handle for edit file. */ 997e32b20dSKelly Yancey static char edit_path[] = "/tmp/camXXXXXX"; 1007e32b20dSKelly Yancey 1017e32b20dSKelly Yancey 1027e32b20dSKelly Yancey /* Function prototypes. */ 1037e32b20dSKelly Yancey static void editentry_create(void *hook, int letter, void *arg, 1047e32b20dSKelly Yancey int count, char *name); 1057e32b20dSKelly Yancey static void editentry_update(void *hook, int letter, void *arg, 1067e32b20dSKelly Yancey int count, char *name); 1077e32b20dSKelly Yancey static int editentry_save(void *hook, char *name); 1087e32b20dSKelly Yancey static struct editentry *editentry_lookup(char *name); 1097e32b20dSKelly Yancey static int editentry_set(char *name, char *newvalue, 1107e32b20dSKelly Yancey int editonly); 1117e32b20dSKelly Yancey static void editlist_populate(struct cam_device *device, 1127e32b20dSKelly Yancey int modepage, int page_control, 1137e32b20dSKelly Yancey int dbd, int retries, int timeout); 1147e32b20dSKelly Yancey static void editlist_save(struct cam_device *device, int modepage, 1157e32b20dSKelly Yancey int page_control, int dbd, int retries, 1167e32b20dSKelly Yancey int timeout); 1177e32b20dSKelly Yancey static void nameentry_create(int pagenum, char *name); 1187e32b20dSKelly Yancey static struct pagename *nameentry_lookup(int pagenum); 11939867613SJohan Karlsson static int load_format(const char *pagedb_path, int page); 1207e32b20dSKelly Yancey static int modepage_write(FILE *file, int editonly); 1217e32b20dSKelly Yancey static int modepage_read(FILE *file); 1227e32b20dSKelly Yancey static void modepage_edit(void); 1237e32b20dSKelly Yancey static void modepage_dump(struct cam_device *device, int page, 1247e32b20dSKelly Yancey int page_control, int dbd, int retries, 1257e32b20dSKelly Yancey int timeout); 1267e32b20dSKelly Yancey static void cleanup_editfile(void); 1277e32b20dSKelly Yancey 1287e32b20dSKelly Yancey 1297e32b20dSKelly Yancey #define returnerr(code) do { \ 1307e32b20dSKelly Yancey errno = code; \ 1317e32b20dSKelly Yancey return (-1); \ 1327e32b20dSKelly Yancey } while (0) 1337e32b20dSKelly Yancey 1347e32b20dSKelly Yancey 1357e32b20dSKelly Yancey #define RTRIM(string) do { \ 1363d438ad6SDavid E. O'Brien int _length; \ 1377e32b20dSKelly Yancey while (isspace(string[_length = strlen(string) - 1])) \ 1387e32b20dSKelly Yancey string[_length] = '\0'; \ 1397e32b20dSKelly Yancey } while (0) 1407e32b20dSKelly Yancey 1417e32b20dSKelly Yancey 1427e32b20dSKelly Yancey static void 14339867613SJohan Karlsson editentry_create(void *hook __unused, int letter, void *arg, int count, 14439867613SJohan Karlsson char *name) 145525689f1SJustin T. Gibbs { 1467e32b20dSKelly Yancey struct editentry *newentry; /* Buffer to hold new entry. */ 147525689f1SJustin T. Gibbs 1487e32b20dSKelly Yancey /* Allocate memory for the new entry and a copy of the entry name. */ 1497e32b20dSKelly Yancey if ((newentry = malloc(sizeof(struct editentry))) == NULL || 1507e32b20dSKelly Yancey (newentry->name = strdup(name)) == NULL) 1517e32b20dSKelly Yancey err(EX_OSERR, NULL); 152525689f1SJustin T. Gibbs 1537e32b20dSKelly Yancey /* Trim any trailing whitespace for the entry name. */ 1547e32b20dSKelly Yancey RTRIM(newentry->name); 155525689f1SJustin T. Gibbs 1567e32b20dSKelly Yancey newentry->editable = (arg != NULL); 1577e32b20dSKelly Yancey newentry->type = letter; 1587e32b20dSKelly Yancey newentry->size = count; /* Placeholder; not accurate. */ 1597e32b20dSKelly Yancey newentry->value.svalue = NULL; 1607e32b20dSKelly Yancey 1617e32b20dSKelly Yancey STAILQ_INSERT_TAIL(&editlist, newentry, link); 162525689f1SJustin T. Gibbs } 163525689f1SJustin T. Gibbs 1647e32b20dSKelly Yancey static void 16539867613SJohan Karlsson editentry_update(void *hook __unused, int letter, void *arg, int count, 16639867613SJohan Karlsson char *name) 167525689f1SJustin T. Gibbs { 1687e32b20dSKelly Yancey struct editentry *dest; /* Buffer to hold entry to update. */ 169525689f1SJustin T. Gibbs 1707e32b20dSKelly Yancey dest = editentry_lookup(name); 1717e32b20dSKelly Yancey assert(dest != NULL); 172525689f1SJustin T. Gibbs 1737e32b20dSKelly Yancey dest->type = letter; 1747e32b20dSKelly Yancey dest->size = count; /* We get the real size now. */ 175525689f1SJustin T. Gibbs 1767e32b20dSKelly Yancey switch (dest->type) { 1777e32b20dSKelly Yancey case 'i': /* Byte-sized integral type. */ 1787e32b20dSKelly Yancey case 'b': /* Bit-sized integral types. */ 1797e32b20dSKelly Yancey case 't': 1807e32b20dSKelly Yancey dest->value.ivalue = (intptr_t)arg; 181525689f1SJustin T. Gibbs break; 182525689f1SJustin T. Gibbs 1837e32b20dSKelly Yancey case 'c': /* Character array. */ 1847e32b20dSKelly Yancey case 'z': /* Null-padded string. */ 1857e32b20dSKelly Yancey editentry_set(name, (char *)arg, 0); 186525689f1SJustin T. Gibbs break; 187525689f1SJustin T. Gibbs default: 188b055b157SGarrett Wollman ; /* NOTREACHED */ 189525689f1SJustin T. Gibbs } 190525689f1SJustin T. Gibbs } 191525689f1SJustin T. Gibbs 192525689f1SJustin T. Gibbs static int 19339867613SJohan Karlsson editentry_save(void *hook __unused, char *name) 194525689f1SJustin T. Gibbs { 1957e32b20dSKelly Yancey struct editentry *src; /* Entry value to save. */ 196525689f1SJustin T. Gibbs 1977e32b20dSKelly Yancey src = editentry_lookup(name); 1987e32b20dSKelly Yancey assert(src != NULL); 199525689f1SJustin T. Gibbs 2007e32b20dSKelly Yancey switch (src->type) { 2017e32b20dSKelly Yancey case 'i': /* Byte-sized integral type. */ 2027e32b20dSKelly Yancey case 'b': /* Bit-sized integral types. */ 2037e32b20dSKelly Yancey case 't': 2047e32b20dSKelly Yancey return (src->value.ivalue); 2057e32b20dSKelly Yancey /* NOTREACHED */ 206525689f1SJustin T. Gibbs 2077e32b20dSKelly Yancey case 'c': /* Character array. */ 2087e32b20dSKelly Yancey case 'z': /* Null-padded string. */ 2097e32b20dSKelly Yancey return ((intptr_t)src->value.svalue); 2107e32b20dSKelly Yancey /* NOTREACHED */ 211525689f1SJustin T. Gibbs 2127e32b20dSKelly Yancey default: 213b055b157SGarrett Wollman ; /* NOTREACHED */ 214525689f1SJustin T. Gibbs } 215525689f1SJustin T. Gibbs 2167e32b20dSKelly Yancey return (0); /* This should never happen. */ 2177e32b20dSKelly Yancey } 2187e32b20dSKelly Yancey 2197e32b20dSKelly Yancey static struct editentry * 2207e32b20dSKelly Yancey editentry_lookup(char *name) 2217e32b20dSKelly Yancey { 2227e32b20dSKelly Yancey struct editentry *scan; 2237e32b20dSKelly Yancey 2247e32b20dSKelly Yancey assert(name != NULL); 2257e32b20dSKelly Yancey 2267e32b20dSKelly Yancey STAILQ_FOREACH(scan, &editlist, link) { 2277e32b20dSKelly Yancey if (strcasecmp(scan->name, name) == 0) 2287e32b20dSKelly Yancey return (scan); 2297e32b20dSKelly Yancey } 2307e32b20dSKelly Yancey 2317e32b20dSKelly Yancey /* Not found during list traversal. */ 2327e32b20dSKelly Yancey return (NULL); 2337e32b20dSKelly Yancey } 2347e32b20dSKelly Yancey 2357e32b20dSKelly Yancey static int 2367e32b20dSKelly Yancey editentry_set(char *name, char *newvalue, int editonly) 2377e32b20dSKelly Yancey { 2387e32b20dSKelly Yancey struct editentry *dest; /* Modepage entry to update. */ 2397e32b20dSKelly Yancey char *cval; /* Pointer to new string value. */ 2407e32b20dSKelly Yancey char *convertend; /* End-of-conversion pointer. */ 2417e32b20dSKelly Yancey int ival; /* New integral value. */ 2427e32b20dSKelly Yancey int resolution; /* Resolution in bits for integer conversion. */ 2437e32b20dSKelly Yancey 2447e32b20dSKelly Yancey /* 2457e32b20dSKelly Yancey * Macro to determine the maximum value of the given size for the current 2467e32b20dSKelly Yancey * resolution. 2477e32b20dSKelly Yancey * XXX Lovely x86's optimize out the case of shifting by 32 and gcc doesn't 2487e32b20dSKelly Yancey * currently workaround it (even for int64's), so we have to kludge it. 2497e32b20dSKelly Yancey */ 2507e32b20dSKelly Yancey #define RESOLUTION_MAX(size) ((resolution * (size) == 32)? \ 25139867613SJohan Karlsson (int)0xffffffff: (1 << (resolution * (size))) - 1) 2527e32b20dSKelly Yancey 2537e32b20dSKelly Yancey assert(newvalue != NULL); 2547e32b20dSKelly Yancey if (*newvalue == '\0') 2557e32b20dSKelly Yancey return (0); /* Nothing to do. */ 2567e32b20dSKelly Yancey 2577e32b20dSKelly Yancey if ((dest = editentry_lookup(name)) == NULL) 2587e32b20dSKelly Yancey returnerr(ENOENT); 2597e32b20dSKelly Yancey if (!dest->editable && editonly) 2607e32b20dSKelly Yancey returnerr(EPERM); 2617e32b20dSKelly Yancey 2627e32b20dSKelly Yancey switch (dest->type) { 2637e32b20dSKelly Yancey case 'i': /* Byte-sized integral type. */ 2647e32b20dSKelly Yancey case 'b': /* Bit-sized integral types. */ 2657e32b20dSKelly Yancey case 't': 2667e32b20dSKelly Yancey /* Convert the value string to an integer. */ 2677e32b20dSKelly Yancey resolution = (dest->type == 'i')? 8: 1; 2687e32b20dSKelly Yancey ival = (int)strtol(newvalue, &convertend, 0); 2697e32b20dSKelly Yancey if (*convertend != '\0') 2707e32b20dSKelly Yancey returnerr(EINVAL); 2717e32b20dSKelly Yancey if (ival > RESOLUTION_MAX(dest->size) || ival < 0) { 2727e32b20dSKelly Yancey int newival = (ival < 0)? 0: RESOLUTION_MAX(dest->size); 2737e32b20dSKelly Yancey warnx("value %d is out of range for entry %s; clipping " 2747e32b20dSKelly Yancey "to %d", ival, name, newival); 2757e32b20dSKelly Yancey ival = newival; 2767e32b20dSKelly Yancey } 2777e32b20dSKelly Yancey if (dest->value.ivalue != ival) 2787e32b20dSKelly Yancey editlist_changed = 1; 2797e32b20dSKelly Yancey dest->value.ivalue = ival; 2807e32b20dSKelly Yancey break; 2817e32b20dSKelly Yancey 2827e32b20dSKelly Yancey case 'c': /* Character array. */ 2837e32b20dSKelly Yancey case 'z': /* Null-padded string. */ 2847e32b20dSKelly Yancey if ((cval = malloc(dest->size + 1)) == NULL) 2857e32b20dSKelly Yancey err(EX_OSERR, NULL); 2867e32b20dSKelly Yancey bzero(cval, dest->size + 1); 2877e32b20dSKelly Yancey strncpy(cval, newvalue, dest->size); 2887e32b20dSKelly Yancey if (dest->type == 'z') { 2897e32b20dSKelly Yancey /* Convert trailing spaces to nulls. */ 29039867613SJohan Karlsson char *convertend2; 2917e32b20dSKelly Yancey 29239867613SJohan Karlsson for (convertend2 = cval + dest->size; 29339867613SJohan Karlsson convertend2 >= cval; convertend2--) { 29439867613SJohan Karlsson if (*convertend2 == ' ') 29539867613SJohan Karlsson *convertend2 = '\0'; 29639867613SJohan Karlsson else if (*convertend2 != '\0') 2977e32b20dSKelly Yancey break; 2987e32b20dSKelly Yancey } 2997e32b20dSKelly Yancey } 3007e32b20dSKelly Yancey if (strncmp(dest->value.svalue, cval, dest->size) == 0) { 3017e32b20dSKelly Yancey /* Nothing changed, free the newly allocated string. */ 3027e32b20dSKelly Yancey free(cval); 3037e32b20dSKelly Yancey break; 3047e32b20dSKelly Yancey } 3057e32b20dSKelly Yancey if (dest->value.svalue != NULL) { 3067e32b20dSKelly Yancey /* Free the current string buffer. */ 3077e32b20dSKelly Yancey free(dest->value.svalue); 3087e32b20dSKelly Yancey dest->value.svalue = NULL; 3097e32b20dSKelly Yancey } 3107e32b20dSKelly Yancey dest->value.svalue = cval; 3117e32b20dSKelly Yancey editlist_changed = 1; 3127e32b20dSKelly Yancey break; 3137e32b20dSKelly Yancey 3147e32b20dSKelly Yancey default: 315b055b157SGarrett Wollman ; /* NOTREACHED */ 3167e32b20dSKelly Yancey } 3177e32b20dSKelly Yancey 3187e32b20dSKelly Yancey return (0); 3197e32b20dSKelly Yancey #undef RESOLUTION_MAX 320525689f1SJustin T. Gibbs } 321525689f1SJustin T. Gibbs 322525689f1SJustin T. Gibbs static void 3237e32b20dSKelly Yancey nameentry_create(int pagenum, char *name) { 3247e32b20dSKelly Yancey struct pagename *newentry; 3257e32b20dSKelly Yancey 3267e32b20dSKelly Yancey if (pagenum < 0 || name == NULL || name[0] == '\0') 3277e32b20dSKelly Yancey return; 3287e32b20dSKelly Yancey 3297e32b20dSKelly Yancey /* Allocate memory for the new entry and a copy of the entry name. */ 3307e32b20dSKelly Yancey if ((newentry = malloc(sizeof(struct pagename))) == NULL || 3317e32b20dSKelly Yancey (newentry->name = strdup(name)) == NULL) 3327e32b20dSKelly Yancey err(EX_OSERR, NULL); 3337e32b20dSKelly Yancey 3347e32b20dSKelly Yancey /* Trim any trailing whitespace for the page name. */ 3357e32b20dSKelly Yancey RTRIM(newentry->name); 3367e32b20dSKelly Yancey 3377e32b20dSKelly Yancey newentry->pagenum = pagenum; 3387e32b20dSKelly Yancey SLIST_INSERT_HEAD(&namelist, newentry, link); 3397e32b20dSKelly Yancey } 3407e32b20dSKelly Yancey 3417e32b20dSKelly Yancey static struct pagename * 3427e32b20dSKelly Yancey nameentry_lookup(int pagenum) { 3437e32b20dSKelly Yancey struct pagename *scan; 3447e32b20dSKelly Yancey 3457e32b20dSKelly Yancey SLIST_FOREACH(scan, &namelist, link) { 3467e32b20dSKelly Yancey if (pagenum == scan->pagenum) 3477e32b20dSKelly Yancey return (scan); 3487e32b20dSKelly Yancey } 3497e32b20dSKelly Yancey 3507e32b20dSKelly Yancey /* Not found during list traversal. */ 3517e32b20dSKelly Yancey return (NULL); 3527e32b20dSKelly Yancey } 3537e32b20dSKelly Yancey 3547e32b20dSKelly Yancey static int 35539867613SJohan Karlsson load_format(const char *pagedb_path, int page) 356525689f1SJustin T. Gibbs { 3577e32b20dSKelly Yancey FILE *pagedb; 3587e32b20dSKelly Yancey char str_pagenum[MAX_PAGENUM_LEN]; 3597e32b20dSKelly Yancey char str_pagename[MAX_PAGENAME_LEN]; 3607e32b20dSKelly Yancey int pagenum; 3617e32b20dSKelly Yancey int depth; /* Quoting depth. */ 3627e32b20dSKelly Yancey int found; 3637e32b20dSKelly Yancey int lineno; 3647e32b20dSKelly Yancey enum { LOCATE, PAGENAME, PAGEDEF } state; 3655cfe0423SPeter Grehan int ch; 3667e32b20dSKelly Yancey char c; 3677e32b20dSKelly Yancey 3687e32b20dSKelly Yancey #define SETSTATE_LOCATE do { \ 3697e32b20dSKelly Yancey str_pagenum[0] = '\0'; \ 3707e32b20dSKelly Yancey str_pagename[0] = '\0'; \ 3717e32b20dSKelly Yancey pagenum = -1; \ 3727e32b20dSKelly Yancey state = LOCATE; \ 3737e32b20dSKelly Yancey } while (0) 3747e32b20dSKelly Yancey 3757e32b20dSKelly Yancey #define SETSTATE_PAGENAME do { \ 3767e32b20dSKelly Yancey str_pagename[0] = '\0'; \ 3777e32b20dSKelly Yancey state = PAGENAME; \ 3787e32b20dSKelly Yancey } while (0) 3797e32b20dSKelly Yancey 3807e32b20dSKelly Yancey #define SETSTATE_PAGEDEF do { \ 3817e32b20dSKelly Yancey format[0] = '\0'; \ 3827e32b20dSKelly Yancey state = PAGEDEF; \ 3837e32b20dSKelly Yancey } while (0) 3847e32b20dSKelly Yancey 3857e32b20dSKelly Yancey #define UPDATE_LINENO do { \ 3867e32b20dSKelly Yancey if (c == '\n') \ 3877e32b20dSKelly Yancey lineno++; \ 3887e32b20dSKelly Yancey } while (0) 3897e32b20dSKelly Yancey 3907e32b20dSKelly Yancey #define BUFFERFULL(buffer) (strlen(buffer) + 1 >= sizeof(buffer)) 3917e32b20dSKelly Yancey 3927e32b20dSKelly Yancey if ((pagedb = fopen(pagedb_path, "r")) == NULL) 3937e32b20dSKelly Yancey returnerr(ENOENT); 3947e32b20dSKelly Yancey 3957e32b20dSKelly Yancey SLIST_INIT(&namelist); 3967e32b20dSKelly Yancey 3977e32b20dSKelly Yancey depth = 0; 3987e32b20dSKelly Yancey lineno = 0; 3997e32b20dSKelly Yancey found = 0; 4007e32b20dSKelly Yancey SETSTATE_LOCATE; 4015cfe0423SPeter Grehan while ((ch = fgetc(pagedb)) != EOF) { 4027e32b20dSKelly Yancey 4037e32b20dSKelly Yancey /* Keep a line count to make error messages more useful. */ 4047e32b20dSKelly Yancey UPDATE_LINENO; 4057e32b20dSKelly Yancey 4067e32b20dSKelly Yancey /* Skip over comments anywhere in the mode database. */ 4075cfe0423SPeter Grehan if (ch == '#') { 4087e32b20dSKelly Yancey do { 4095cfe0423SPeter Grehan ch = fgetc(pagedb); 4105cfe0423SPeter Grehan } while (ch != '\n' && ch != EOF); 4117e32b20dSKelly Yancey UPDATE_LINENO; 4127e32b20dSKelly Yancey continue; 4137e32b20dSKelly Yancey } 4145cfe0423SPeter Grehan c = ch; 4157e32b20dSKelly Yancey 4167e32b20dSKelly Yancey /* Strip out newline characters. */ 4177e32b20dSKelly Yancey if (c == '\n') 4187e32b20dSKelly Yancey continue; 4197e32b20dSKelly Yancey 4207e32b20dSKelly Yancey /* Keep track of the nesting depth for braces. */ 4217e32b20dSKelly Yancey if (c == PAGEDEF_START) 4227e32b20dSKelly Yancey depth++; 4237e32b20dSKelly Yancey else if (c == PAGEDEF_END) { 4247e32b20dSKelly Yancey depth--; 4257e32b20dSKelly Yancey if (depth < 0) { 4267e32b20dSKelly Yancey errx(EX_OSFILE, "%s:%d: %s", pagedb_path, 4277e32b20dSKelly Yancey lineno, "mismatched bracket"); 4287e32b20dSKelly Yancey } 4297e32b20dSKelly Yancey } 4307e32b20dSKelly Yancey 4317e32b20dSKelly Yancey switch (state) { 4327e32b20dSKelly Yancey case LOCATE: 4337e32b20dSKelly Yancey /* 4347e32b20dSKelly Yancey * Locate the page the user is interested in, skipping 4357e32b20dSKelly Yancey * all others. 4367e32b20dSKelly Yancey */ 4377e32b20dSKelly Yancey if (isspace(c)) { 4387e32b20dSKelly Yancey /* Ignore all whitespace between pages. */ 4397e32b20dSKelly Yancey break; 4407e32b20dSKelly Yancey } else if (depth == 0 && c == PAGEENTRY_END) { 4417e32b20dSKelly Yancey /* 4427e32b20dSKelly Yancey * A page entry terminator will reset page 4437e32b20dSKelly Yancey * scanning (useful for assigning names to 4447e32b20dSKelly Yancey * modes without providing a mode definition). 4457e32b20dSKelly Yancey */ 4467e32b20dSKelly Yancey /* Record the name of this page. */ 4477e32b20dSKelly Yancey pagenum = strtol(str_pagenum, NULL, 0); 4487e32b20dSKelly Yancey nameentry_create(pagenum, str_pagename); 4497e32b20dSKelly Yancey SETSTATE_LOCATE; 4507e32b20dSKelly Yancey } else if (depth == 0 && c == PAGENAME_START) { 4517e32b20dSKelly Yancey SETSTATE_PAGENAME; 4527e32b20dSKelly Yancey } else if (c == PAGEDEF_START) { 4537e32b20dSKelly Yancey pagenum = strtol(str_pagenum, NULL, 0); 4547e32b20dSKelly Yancey if (depth == 1) { 4557e32b20dSKelly Yancey /* Record the name of this page. */ 4567e32b20dSKelly Yancey nameentry_create(pagenum, str_pagename); 4577e32b20dSKelly Yancey /* 4587e32b20dSKelly Yancey * Only record the format if this is 4597e32b20dSKelly Yancey * the page we are interested in. 4607e32b20dSKelly Yancey */ 4617e32b20dSKelly Yancey if (page == pagenum && !found) 4627e32b20dSKelly Yancey SETSTATE_PAGEDEF; 4637e32b20dSKelly Yancey } 4647e32b20dSKelly Yancey } else if (c == PAGEDEF_END) { 4657e32b20dSKelly Yancey /* Reset the processor state. */ 4667e32b20dSKelly Yancey SETSTATE_LOCATE; 4677e32b20dSKelly Yancey } else if (depth == 0 && ! BUFFERFULL(str_pagenum)) { 4687e32b20dSKelly Yancey strncat(str_pagenum, &c, 1); 4697e32b20dSKelly Yancey } else if (depth == 0) { 470024ae004SRuslan Ermilov errx(EX_OSFILE, "%s:%d: %s %zd %s", pagedb_path, 4717e32b20dSKelly Yancey lineno, "page identifier exceeds", 4727e32b20dSKelly Yancey sizeof(str_pagenum) - 1, "characters"); 4737e32b20dSKelly Yancey } 4747e32b20dSKelly Yancey break; 4757e32b20dSKelly Yancey 4767e32b20dSKelly Yancey case PAGENAME: 4777e32b20dSKelly Yancey if (c == PAGENAME_END) { 4787e32b20dSKelly Yancey /* 4797e32b20dSKelly Yancey * Return to LOCATE state without resetting the 4807e32b20dSKelly Yancey * page number buffer. 4817e32b20dSKelly Yancey */ 4827e32b20dSKelly Yancey state = LOCATE; 4837e32b20dSKelly Yancey } else if (! BUFFERFULL(str_pagename)) { 4847e32b20dSKelly Yancey strncat(str_pagename, &c, 1); 4857e32b20dSKelly Yancey } else { 486024ae004SRuslan Ermilov errx(EX_OSFILE, "%s:%d: %s %zd %s", pagedb_path, 4877e32b20dSKelly Yancey lineno, "page name exceeds", 4887e32b20dSKelly Yancey sizeof(str_pagenum) - 1, "characters"); 4897e32b20dSKelly Yancey } 4907e32b20dSKelly Yancey break; 4917e32b20dSKelly Yancey 4927e32b20dSKelly Yancey case PAGEDEF: 4937e32b20dSKelly Yancey /* 4947e32b20dSKelly Yancey * Transfer the page definition into a format buffer 4957e32b20dSKelly Yancey * suitable for use with CDB encoding/decoding routines. 4967e32b20dSKelly Yancey */ 4977e32b20dSKelly Yancey if (depth == 0) { 4987e32b20dSKelly Yancey found = 1; 4997e32b20dSKelly Yancey SETSTATE_LOCATE; 5007e32b20dSKelly Yancey } else if (! BUFFERFULL(format)) { 5017e32b20dSKelly Yancey strncat(format, &c, 1); 5027e32b20dSKelly Yancey } else { 503024ae004SRuslan Ermilov errx(EX_OSFILE, "%s:%d: %s %zd %s", pagedb_path, 5047e32b20dSKelly Yancey lineno, "page definition exceeds", 5057e32b20dSKelly Yancey sizeof(format) - 1, "characters"); 5067e32b20dSKelly Yancey } 5077e32b20dSKelly Yancey break; 5087e32b20dSKelly Yancey 5097e32b20dSKelly Yancey default: 510b055b157SGarrett Wollman ; /* NOTREACHED */ 5117e32b20dSKelly Yancey } 5127e32b20dSKelly Yancey 5137e32b20dSKelly Yancey /* Repeat processing loop with next character. */ 5147e32b20dSKelly Yancey } 5157e32b20dSKelly Yancey 5167e32b20dSKelly Yancey if (ferror(pagedb)) 5177e32b20dSKelly Yancey err(EX_OSFILE, "%s", pagedb_path); 5187e32b20dSKelly Yancey 5197e32b20dSKelly Yancey /* Close the SCSI page database. */ 5207e32b20dSKelly Yancey fclose(pagedb); 5217e32b20dSKelly Yancey 5227e32b20dSKelly Yancey if (!found) /* Never found a matching page. */ 5237e32b20dSKelly Yancey returnerr(ESRCH); 5247e32b20dSKelly Yancey 5257e32b20dSKelly Yancey return (0); 5267e32b20dSKelly Yancey } 5277e32b20dSKelly Yancey 5287e32b20dSKelly Yancey static void 5297e32b20dSKelly Yancey editlist_populate(struct cam_device *device, int modepage, int page_control, 5307e32b20dSKelly Yancey int dbd, int retries, int timeout) 5317e32b20dSKelly Yancey { 5327e32b20dSKelly Yancey u_int8_t data[MAX_COMMAND_SIZE];/* Buffer to hold sense data. */ 5337e32b20dSKelly Yancey u_int8_t *mode_pars; /* Pointer to modepage params. */ 5347e32b20dSKelly Yancey struct scsi_mode_header_6 *mh; /* Location of mode header. */ 5357e32b20dSKelly Yancey struct scsi_mode_page_header *mph; 5367e32b20dSKelly Yancey 5377e32b20dSKelly Yancey STAILQ_INIT(&editlist); 5387e32b20dSKelly Yancey 5397e32b20dSKelly Yancey /* Fetch changeable values; use to build initial editlist. */ 5407e32b20dSKelly Yancey mode_sense(device, modepage, 1, dbd, retries, timeout, data, 5417e32b20dSKelly Yancey sizeof(data)); 5427e32b20dSKelly Yancey 5437e32b20dSKelly Yancey mh = (struct scsi_mode_header_6 *)data; 5447e32b20dSKelly Yancey mph = MODE_PAGE_HEADER(mh); 5457e32b20dSKelly Yancey mode_pars = MODE_PAGE_DATA(mph); 5467e32b20dSKelly Yancey 5477e32b20dSKelly Yancey /* Decode the value data, creating edit_entries for each value. */ 5487e32b20dSKelly Yancey buff_decode_visit(mode_pars, mh->data_length, format, 5497e32b20dSKelly Yancey editentry_create, 0); 5507e32b20dSKelly Yancey 5517e32b20dSKelly Yancey /* Fetch the current/saved values; use to set editentry values. */ 5527e32b20dSKelly Yancey mode_sense(device, modepage, page_control, dbd, retries, timeout, data, 5537e32b20dSKelly Yancey sizeof(data)); 5547e32b20dSKelly Yancey buff_decode_visit(mode_pars, mh->data_length, format, 5557e32b20dSKelly Yancey editentry_update, 0); 5567e32b20dSKelly Yancey } 5577e32b20dSKelly Yancey 5587e32b20dSKelly Yancey static void 5597e32b20dSKelly Yancey editlist_save(struct cam_device *device, int modepage, int page_control, 5607e32b20dSKelly Yancey int dbd, int retries, int timeout) 5617e32b20dSKelly Yancey { 5627e32b20dSKelly Yancey u_int8_t data[MAX_COMMAND_SIZE];/* Buffer to hold sense data. */ 5637e32b20dSKelly Yancey u_int8_t *mode_pars; /* Pointer to modepage params. */ 5647e32b20dSKelly Yancey struct scsi_mode_header_6 *mh; /* Location of mode header. */ 5657e32b20dSKelly Yancey struct scsi_mode_page_header *mph; 5667e32b20dSKelly Yancey 5677e32b20dSKelly Yancey /* Make sure that something changed before continuing. */ 5687e32b20dSKelly Yancey if (! editlist_changed) 5697e32b20dSKelly Yancey return; 5707e32b20dSKelly Yancey 5717e32b20dSKelly Yancey /* 5727e32b20dSKelly Yancey * Preload the CDB buffer with the current mode page data. 5737e32b20dSKelly Yancey * XXX If buff_encode_visit would return the number of bytes encoded 5747e32b20dSKelly Yancey * we *should* use that to build a header from scratch. As it is 5757e32b20dSKelly Yancey * now, we need mode_sense to find out the page length. 5767e32b20dSKelly Yancey */ 5777e32b20dSKelly Yancey mode_sense(device, modepage, page_control, dbd, retries, timeout, data, 5787e32b20dSKelly Yancey sizeof(data)); 5797e32b20dSKelly Yancey 5807e32b20dSKelly Yancey /* Initial headers & offsets. */ 5817e32b20dSKelly Yancey mh = (struct scsi_mode_header_6 *)data; 5827e32b20dSKelly Yancey mph = MODE_PAGE_HEADER(mh); 5837e32b20dSKelly Yancey mode_pars = MODE_PAGE_DATA(mph); 5847e32b20dSKelly Yancey 5857e32b20dSKelly Yancey /* Encode the value data to be passed back to the device. */ 58634f71eebSJohn Baldwin buff_encode_visit(mode_pars, mh->data_length, format, 5877e32b20dSKelly Yancey editentry_save, 0); 5887e32b20dSKelly Yancey 5897e32b20dSKelly Yancey /* Eliminate block descriptors. */ 5907e32b20dSKelly Yancey bcopy(mph, ((u_int8_t *)mh) + sizeof(*mh), 5917e32b20dSKelly Yancey sizeof(*mph) + mph->page_length); 5927e32b20dSKelly Yancey 5937e32b20dSKelly Yancey /* Recalculate headers & offsets. */ 5947e32b20dSKelly Yancey mh->blk_desc_len = 0; /* No block descriptors. */ 5957e32b20dSKelly Yancey mh->dev_spec = 0; /* Clear device-specific parameters. */ 5967e32b20dSKelly Yancey mph = MODE_PAGE_HEADER(mh); 5977e32b20dSKelly Yancey mode_pars = MODE_PAGE_DATA(mph); 5987e32b20dSKelly Yancey 5997e32b20dSKelly Yancey mph->page_code &= SMS_PAGE_CODE;/* Isolate just the page code. */ 6007e32b20dSKelly Yancey mh->data_length = 0; /* Reserved for MODE SELECT command. */ 6017e32b20dSKelly Yancey 6027e32b20dSKelly Yancey /* 6037e32b20dSKelly Yancey * Write the changes back to the device. If the user editted control 6047e32b20dSKelly Yancey * page 3 (saved values) then request the changes be permanently 6057e32b20dSKelly Yancey * recorded. 6067e32b20dSKelly Yancey */ 607354a0adaSKelly Yancey mode_select(device, 608354a0adaSKelly Yancey (page_control << PAGE_CTRL_SHIFT == SMS_PAGE_CTRL_SAVED), 609354a0adaSKelly Yancey retries, timeout, (u_int8_t *)mh, 610354a0adaSKelly Yancey sizeof(*mh) + mh->blk_desc_len + sizeof(*mph) + mph->page_length); 6117e32b20dSKelly Yancey } 6127e32b20dSKelly Yancey 6137e32b20dSKelly Yancey static int 6147e32b20dSKelly Yancey modepage_write(FILE *file, int editonly) 6157e32b20dSKelly Yancey { 6167e32b20dSKelly Yancey struct editentry *scan; 6177e32b20dSKelly Yancey int written = 0; 6187e32b20dSKelly Yancey 6197e32b20dSKelly Yancey STAILQ_FOREACH(scan, &editlist, link) { 6207e32b20dSKelly Yancey if (scan->editable || !editonly) { 6217e32b20dSKelly Yancey written++; 6227e32b20dSKelly Yancey if (scan->type == 'c' || scan->type == 'z') { 6237e32b20dSKelly Yancey fprintf(file, "%s: %s\n", scan->name, 6247e32b20dSKelly Yancey scan->value.svalue); 6257e32b20dSKelly Yancey } else { 6267e32b20dSKelly Yancey fprintf(file, "%s: %d\n", scan->name, 6277e32b20dSKelly Yancey scan->value.ivalue); 6287e32b20dSKelly Yancey } 6297e32b20dSKelly Yancey } 6307e32b20dSKelly Yancey } 6317e32b20dSKelly Yancey return (written); 6327e32b20dSKelly Yancey } 6337e32b20dSKelly Yancey 6347e32b20dSKelly Yancey static int 6357e32b20dSKelly Yancey modepage_read(FILE *file) 6367e32b20dSKelly Yancey { 6377e32b20dSKelly Yancey char *buffer; /* Pointer to dynamic line buffer. */ 6387e32b20dSKelly Yancey char *line; /* Pointer to static fgetln buffer. */ 6397e32b20dSKelly Yancey char *name; /* Name portion of the line buffer. */ 6407e32b20dSKelly Yancey char *value; /* Value portion of line buffer. */ 641c7cf7aa6SJohan Karlsson size_t length; /* Length of static fgetln buffer. */ 6427e32b20dSKelly Yancey 6437e32b20dSKelly Yancey #define ABORT_READ(message, param) do { \ 6447e32b20dSKelly Yancey warnx(message, param); \ 6457e32b20dSKelly Yancey free(buffer); \ 6467e32b20dSKelly Yancey returnerr(EAGAIN); \ 6477e32b20dSKelly Yancey } while (0) 6487e32b20dSKelly Yancey 6497e32b20dSKelly Yancey while ((line = fgetln(file, &length)) != NULL) { 6507e32b20dSKelly Yancey /* Trim trailing whitespace (including optional newline). */ 6517e32b20dSKelly Yancey while (length > 0 && isspace(line[length - 1])) 6527e32b20dSKelly Yancey length--; 6537e32b20dSKelly Yancey 6547e32b20dSKelly Yancey /* Allocate a buffer to hold the line + terminating null. */ 6557e32b20dSKelly Yancey if ((buffer = malloc(length + 1)) == NULL) 6567e32b20dSKelly Yancey err(EX_OSERR, NULL); 6577e32b20dSKelly Yancey memcpy(buffer, line, length); 6587e32b20dSKelly Yancey buffer[length] = '\0'; 6597e32b20dSKelly Yancey 6607e32b20dSKelly Yancey /* Strip out comments. */ 6617e32b20dSKelly Yancey if ((value = strchr(buffer, '#')) != NULL) 6627e32b20dSKelly Yancey *value = '\0'; 6637e32b20dSKelly Yancey 6647e32b20dSKelly Yancey /* The name is first in the buffer. Trim whitespace.*/ 6657e32b20dSKelly Yancey name = buffer; 6667e32b20dSKelly Yancey RTRIM(name); 6677e32b20dSKelly Yancey while (isspace(*name)) 6687e32b20dSKelly Yancey name++; 6697e32b20dSKelly Yancey 6707e32b20dSKelly Yancey /* Skip empty lines. */ 6717e32b20dSKelly Yancey if (strlen(name) == 0) 6727e32b20dSKelly Yancey continue; 6737e32b20dSKelly Yancey 6747e32b20dSKelly Yancey /* The name ends at the colon; the value starts there. */ 6757e32b20dSKelly Yancey if ((value = strrchr(buffer, ':')) == NULL) 6767e32b20dSKelly Yancey ABORT_READ("no value associated with %s", name); 6777e32b20dSKelly Yancey *value = '\0'; /* Null-terminate name. */ 6787e32b20dSKelly Yancey value++; /* Value starts afterwards. */ 6797e32b20dSKelly Yancey 6807e32b20dSKelly Yancey /* Trim leading and trailing whitespace. */ 6817e32b20dSKelly Yancey RTRIM(value); 6827e32b20dSKelly Yancey while (isspace(*value)) 6837e32b20dSKelly Yancey value++; 6847e32b20dSKelly Yancey 6857e32b20dSKelly Yancey /* Make sure there is a value left. */ 6867e32b20dSKelly Yancey if (strlen(value) == 0) 6877e32b20dSKelly Yancey ABORT_READ("no value associated with %s", name); 6887e32b20dSKelly Yancey 6897e32b20dSKelly Yancey /* Update our in-memory copy of the modepage entry value. */ 6907e32b20dSKelly Yancey if (editentry_set(name, value, 1) != 0) { 6917e32b20dSKelly Yancey if (errno == ENOENT) { 6927e32b20dSKelly Yancey /* No entry by the name. */ 6937e32b20dSKelly Yancey ABORT_READ("no such modepage entry \"%s\"", 6947e32b20dSKelly Yancey name); 6957e32b20dSKelly Yancey } else if (errno == EINVAL) { 6967e32b20dSKelly Yancey /* Invalid value. */ 6977e32b20dSKelly Yancey ABORT_READ("Invalid value for entry \"%s\"", 6987e32b20dSKelly Yancey name); 6997e32b20dSKelly Yancey } else if (errno == ERANGE) { 7007e32b20dSKelly Yancey /* Value out of range for entry type. */ 7017e32b20dSKelly Yancey ABORT_READ("value out of range for %s", name); 7027e32b20dSKelly Yancey } else if (errno == EPERM) { 7037e32b20dSKelly Yancey /* Entry is not editable; not fatal. */ 7047e32b20dSKelly Yancey warnx("modepage entry \"%s\" is read-only; " 7057e32b20dSKelly Yancey "skipping.", name); 7067e32b20dSKelly Yancey } 7077e32b20dSKelly Yancey } 7087e32b20dSKelly Yancey 7097e32b20dSKelly Yancey free(buffer); 7107e32b20dSKelly Yancey } 7117e32b20dSKelly Yancey return (ferror(file)? -1: 0); 7127e32b20dSKelly Yancey 7137e32b20dSKelly Yancey #undef ABORT_READ 7147e32b20dSKelly Yancey } 7157e32b20dSKelly Yancey 7167e32b20dSKelly Yancey static void 7177e32b20dSKelly Yancey modepage_edit(void) 7187e32b20dSKelly Yancey { 71939867613SJohan Karlsson const char *editor; 7207e32b20dSKelly Yancey char *commandline; 7217e32b20dSKelly Yancey int fd; 7227e32b20dSKelly Yancey int written; 7237e32b20dSKelly Yancey 7247e32b20dSKelly Yancey if (!isatty(fileno(stdin))) { 7257e32b20dSKelly Yancey /* Not a tty, read changes from stdin. */ 7267e32b20dSKelly Yancey modepage_read(stdin); 7277e32b20dSKelly Yancey return; 7287e32b20dSKelly Yancey } 7297e32b20dSKelly Yancey 7307e32b20dSKelly Yancey /* Lookup editor to invoke. */ 7317e32b20dSKelly Yancey if ((editor = getenv("EDITOR")) == NULL) 7327e32b20dSKelly Yancey editor = DEFAULT_EDITOR; 7337e32b20dSKelly Yancey 7347e32b20dSKelly Yancey /* Create temp file for editor to modify. */ 7357e32b20dSKelly Yancey if ((fd = mkstemp(edit_path)) == -1) 7367e32b20dSKelly Yancey errx(EX_CANTCREAT, "mkstemp failed"); 7377e32b20dSKelly Yancey 7387e32b20dSKelly Yancey atexit(cleanup_editfile); 7397e32b20dSKelly Yancey 7407e32b20dSKelly Yancey if ((edit_file = fdopen(fd, "w")) == NULL) 7417e32b20dSKelly Yancey err(EX_NOINPUT, "%s", edit_path); 7427e32b20dSKelly Yancey 7437e32b20dSKelly Yancey written = modepage_write(edit_file, 1); 744525689f1SJustin T. Gibbs 745525689f1SJustin T. Gibbs fclose(edit_file); 7467e32b20dSKelly Yancey edit_file = NULL; 747525689f1SJustin T. Gibbs 7487e32b20dSKelly Yancey if (written == 0) { 7497e32b20dSKelly Yancey warnx("no editable entries"); 7507e32b20dSKelly Yancey cleanup_editfile(); 7517e32b20dSKelly Yancey return; 7527e32b20dSKelly Yancey } 753525689f1SJustin T. Gibbs 7547e32b20dSKelly Yancey /* 7557e32b20dSKelly Yancey * Allocate memory to hold the command line (the 2 extra characters 7567e32b20dSKelly Yancey * are to hold the argument separator (a space), and the terminating 7577e32b20dSKelly Yancey * null character. 7587e32b20dSKelly Yancey */ 7597e32b20dSKelly Yancey commandline = malloc(strlen(editor) + strlen(edit_path) + 2); 7607e32b20dSKelly Yancey if (commandline == NULL) 7617e32b20dSKelly Yancey err(EX_OSERR, NULL); 7627e32b20dSKelly Yancey sprintf(commandline, "%s %s", editor, edit_path); 7637e32b20dSKelly Yancey 7647e32b20dSKelly Yancey /* Invoke the editor on the temp file. */ 7657e32b20dSKelly Yancey if (system(commandline) == -1) 7667e32b20dSKelly Yancey err(EX_UNAVAILABLE, "could not invoke %s", editor); 7677e32b20dSKelly Yancey free(commandline); 7687e32b20dSKelly Yancey 7697e32b20dSKelly Yancey if ((edit_file = fopen(edit_path, "r")) == NULL) 7707e32b20dSKelly Yancey err(EX_NOINPUT, "%s", edit_path); 7717e32b20dSKelly Yancey 7727e32b20dSKelly Yancey /* Read any changes made to the temp file. */ 7737e32b20dSKelly Yancey modepage_read(edit_file); 7747e32b20dSKelly Yancey 7757e32b20dSKelly Yancey cleanup_editfile(); 7767e32b20dSKelly Yancey } 7777e32b20dSKelly Yancey 7787e32b20dSKelly Yancey static void 7797e32b20dSKelly Yancey modepage_dump(struct cam_device *device, int page, int page_control, int dbd, 7807e32b20dSKelly Yancey int retries, int timeout) 7817e32b20dSKelly Yancey { 7827e32b20dSKelly Yancey u_int8_t data[MAX_COMMAND_SIZE];/* Buffer to hold sense data. */ 7837e32b20dSKelly Yancey u_int8_t *mode_pars; /* Pointer to modepage params. */ 7847e32b20dSKelly Yancey struct scsi_mode_header_6 *mh; /* Location of mode header. */ 7857e32b20dSKelly Yancey struct scsi_mode_page_header *mph; 78639867613SJohan Karlsson int indx; /* Index for scanning mode params. */ 7877e32b20dSKelly Yancey 7887e32b20dSKelly Yancey mode_sense(device, page, page_control, dbd, retries, timeout, data, 7897e32b20dSKelly Yancey sizeof(data)); 7907e32b20dSKelly Yancey 7917e32b20dSKelly Yancey mh = (struct scsi_mode_header_6 *)data; 7927e32b20dSKelly Yancey mph = MODE_PAGE_HEADER(mh); 7937e32b20dSKelly Yancey mode_pars = MODE_PAGE_DATA(mph); 7947e32b20dSKelly Yancey 7957e32b20dSKelly Yancey /* Print the raw mode page data with newlines each 8 bytes. */ 79639867613SJohan Karlsson for (indx = 0; indx < mph->page_length; indx++) { 79739867613SJohan Karlsson printf("%02x%c",mode_pars[indx], 79839867613SJohan Karlsson (((indx + 1) % 8) == 0) ? '\n' : ' '); 7997e32b20dSKelly Yancey } 8007e32b20dSKelly Yancey putchar('\n'); 8017e32b20dSKelly Yancey } 8027e32b20dSKelly Yancey 8037e32b20dSKelly Yancey static void 8047e32b20dSKelly Yancey cleanup_editfile(void) 8057e32b20dSKelly Yancey { 8067e32b20dSKelly Yancey if (edit_file == NULL) 8077e32b20dSKelly Yancey return; 8087e32b20dSKelly Yancey if (fclose(edit_file) != 0 || unlink(edit_path) != 0) 8097e32b20dSKelly Yancey warn("%s", edit_path); 8107e32b20dSKelly Yancey edit_file = NULL; 811525689f1SJustin T. Gibbs } 812525689f1SJustin T. Gibbs 813525689f1SJustin T. Gibbs void 814525689f1SJustin T. Gibbs mode_edit(struct cam_device *device, int page, int page_control, int dbd, 8157e32b20dSKelly Yancey int edit, int binary, int retry_count, int timeout) 816525689f1SJustin T. Gibbs { 81739867613SJohan Karlsson const char *pagedb_path; /* Path to modepage database. */ 818525689f1SJustin T. Gibbs 8197e32b20dSKelly Yancey if (edit && binary) 8207e32b20dSKelly Yancey errx(EX_USAGE, "cannot edit in binary mode."); 821525689f1SJustin T. Gibbs 8227e32b20dSKelly Yancey if (! binary) { 8237e32b20dSKelly Yancey if ((pagedb_path = getenv("SCSI_MODES")) == NULL) 8247e32b20dSKelly Yancey pagedb_path = DEFAULT_SCSI_MODE_DB; 825525689f1SJustin T. Gibbs 8267e32b20dSKelly Yancey if (load_format(pagedb_path, page) != 0 && (edit || verbose)) { 8277e32b20dSKelly Yancey if (errno == ENOENT) { 8287e32b20dSKelly Yancey /* Modepage database file not found. */ 8297e32b20dSKelly Yancey warn("cannot open modepage database \"%s\"", 8307e32b20dSKelly Yancey pagedb_path); 8317e32b20dSKelly Yancey } else if (errno == ESRCH) { 8327e32b20dSKelly Yancey /* Modepage entry not found in database. */ 8337e32b20dSKelly Yancey warnx("modepage %d not found in database" 8347e32b20dSKelly Yancey "\"%s\"", page, pagedb_path); 8357e32b20dSKelly Yancey } 8367e32b20dSKelly Yancey /* We can recover in display mode, otherwise we exit. */ 8377e32b20dSKelly Yancey if (!edit) { 8387e32b20dSKelly Yancey warnx("reverting to binary display only"); 8397e32b20dSKelly Yancey binary = 1; 8407e32b20dSKelly Yancey } else 8417e32b20dSKelly Yancey exit(EX_OSFILE); 8427e32b20dSKelly Yancey } 8437e32b20dSKelly Yancey 8447e32b20dSKelly Yancey editlist_populate(device, page, page_control, dbd, retry_count, 8457e32b20dSKelly Yancey timeout); 846525689f1SJustin T. Gibbs } 847525689f1SJustin T. Gibbs 848525689f1SJustin T. Gibbs if (edit) { 849354a0adaSKelly Yancey if (page_control << PAGE_CTRL_SHIFT != SMS_PAGE_CTRL_CURRENT && 850354a0adaSKelly Yancey page_control << PAGE_CTRL_SHIFT != SMS_PAGE_CTRL_SAVED) 8517e32b20dSKelly Yancey errx(EX_USAGE, "it only makes sense to edit page 0 " 852525689f1SJustin T. Gibbs "(current) or page 3 (saved values)"); 8537e32b20dSKelly Yancey modepage_edit(); 8547e32b20dSKelly Yancey editlist_save(device, page, page_control, dbd, retry_count, 8557e32b20dSKelly Yancey timeout); 8567e32b20dSKelly Yancey } else if (binary || STAILQ_EMPTY(&editlist)) { 8577e32b20dSKelly Yancey /* Display without formatting information. */ 8587e32b20dSKelly Yancey modepage_dump(device, page, page_control, dbd, retry_count, 8597e32b20dSKelly Yancey timeout); 860525689f1SJustin T. Gibbs } else { 8617e32b20dSKelly Yancey /* Display with format. */ 8627e32b20dSKelly Yancey modepage_write(stdout, 0); 8637e32b20dSKelly Yancey } 8647e32b20dSKelly Yancey } 8657e32b20dSKelly Yancey 8667e32b20dSKelly Yancey void 8677e32b20dSKelly Yancey mode_list(struct cam_device *device, int page_control, int dbd, 8687e32b20dSKelly Yancey int retry_count, int timeout) 8697e32b20dSKelly Yancey { 8707e32b20dSKelly Yancey u_int8_t data[MAX_COMMAND_SIZE];/* Buffer to hold sense data. */ 8717e32b20dSKelly Yancey u_int8_t *mode_pars; /* Pointer to modepage params. */ 8727e32b20dSKelly Yancey struct scsi_mode_header_6 *mh; /* Location of mode header. */ 8737e32b20dSKelly Yancey struct scsi_mode_page_header *mph; 8747e32b20dSKelly Yancey struct pagename *nameentry; 87539867613SJohan Karlsson const char *pagedb_path; 8767e32b20dSKelly Yancey int len; 8777e32b20dSKelly Yancey 8787e32b20dSKelly Yancey if ((pagedb_path = getenv("SCSI_MODES")) == NULL) 8797e32b20dSKelly Yancey pagedb_path = DEFAULT_SCSI_MODE_DB; 8807e32b20dSKelly Yancey 8817e32b20dSKelly Yancey if (load_format(pagedb_path, 0) != 0 && verbose && errno == ENOENT) { 8827e32b20dSKelly Yancey /* Modepage database file not found. */ 8837e32b20dSKelly Yancey warn("cannot open modepage database \"%s\"", pagedb_path); 8847e32b20dSKelly Yancey } 8857e32b20dSKelly Yancey 8867e32b20dSKelly Yancey /* Build the list of all mode pages by querying the "all pages" page. */ 8877e32b20dSKelly Yancey mode_sense(device, SMS_ALL_PAGES_PAGE, page_control, dbd, retry_count, 8887e32b20dSKelly Yancey timeout, data, sizeof(data)); 8897e32b20dSKelly Yancey 8907e32b20dSKelly Yancey mh = (struct scsi_mode_header_6 *)data; 8917e32b20dSKelly Yancey len = mh->blk_desc_len; /* Skip block descriptors. */ 8927e32b20dSKelly Yancey /* Iterate through the pages in the reply. */ 8937e32b20dSKelly Yancey while (len < mh->data_length) { 8947e32b20dSKelly Yancey /* Locate the next mode page header. */ 8957e32b20dSKelly Yancey mph = (struct scsi_mode_page_header *) 8967e32b20dSKelly Yancey ((intptr_t)mh + sizeof(*mh) + len); 8977e32b20dSKelly Yancey mode_pars = MODE_PAGE_DATA(mph); 8987e32b20dSKelly Yancey 8997e32b20dSKelly Yancey mph->page_code &= SMS_PAGE_CODE; 9007e32b20dSKelly Yancey nameentry = nameentry_lookup(mph->page_code); 9017e32b20dSKelly Yancey 9027e32b20dSKelly Yancey if (nameentry == NULL || nameentry->name == NULL) 9037e32b20dSKelly Yancey printf("0x%02x\n", mph->page_code); 9047e32b20dSKelly Yancey else 9057e32b20dSKelly Yancey printf("0x%02x\t%s\n", mph->page_code, 9067e32b20dSKelly Yancey nameentry->name); 9077e32b20dSKelly Yancey len += mph->page_length + sizeof(*mph); 908525689f1SJustin T. Gibbs } 909525689f1SJustin T. Gibbs } 910