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 29525689f1SJustin T. Gibbs #ifndef lint 30525689f1SJustin T. Gibbs static const char rcsid[] = 317f3dea24SPeter Wemm "$FreeBSD$"; 32525689f1SJustin T. Gibbs #endif /* not lint */ 33525689f1SJustin T. Gibbs 347e32b20dSKelly Yancey #include <sys/queue.h> 357e32b20dSKelly Yancey #include <sys/types.h> 367e32b20dSKelly Yancey 377e32b20dSKelly Yancey #include <assert.h> 38525689f1SJustin T. Gibbs #include <ctype.h> 39525689f1SJustin T. Gibbs #include <err.h> 40525689f1SJustin T. Gibbs #include <errno.h> 41525689f1SJustin T. Gibbs #include <stdlib.h> 427e32b20dSKelly Yancey #include <string.h> 43525689f1SJustin T. Gibbs #include <stdio.h> 447e32b20dSKelly Yancey #include <sysexits.h> 45525689f1SJustin T. Gibbs #include <unistd.h> 46525689f1SJustin T. Gibbs 477e32b20dSKelly Yancey #include <cam/scsi/scsi_all.h> 48525689f1SJustin T. Gibbs #include <cam/cam.h> 49525689f1SJustin T. Gibbs #include <cam/cam_ccb.h> 50525689f1SJustin T. Gibbs #include <camlib.h> 51525689f1SJustin T. Gibbs #include "camcontrol.h" 52525689f1SJustin T. Gibbs 53525689f1SJustin T. Gibbs int verbose = 0; 54525689f1SJustin T. Gibbs 557e32b20dSKelly Yancey #define DEFAULT_SCSI_MODE_DB "/usr/share/misc/scsi_modes" 567e32b20dSKelly Yancey #define DEFAULT_EDITOR "vi" 577e32b20dSKelly Yancey #define MAX_FORMAT_SPEC 4096 /* Max CDB format specifier. */ 587e32b20dSKelly Yancey #define MAX_PAGENUM_LEN 10 /* Max characters in page num. */ 597e32b20dSKelly Yancey #define MAX_PAGENAME_LEN 64 /* Max characters in page name. */ 607e32b20dSKelly Yancey #define PAGEDEF_START '{' /* Page definition delimiter. */ 617e32b20dSKelly Yancey #define PAGEDEF_END '}' /* Page definition delimiter. */ 627e32b20dSKelly Yancey #define PAGENAME_START '"' /* Page name delimiter. */ 637e32b20dSKelly Yancey #define PAGENAME_END '"' /* Page name delimiter. */ 647e32b20dSKelly Yancey #define PAGEENTRY_END ';' /* Page entry terminator (optional). */ 657e32b20dSKelly Yancey #define MAX_COMMAND_SIZE 255 /* Mode/Log sense data buffer size. */ 66354a0adaSKelly Yancey #define PAGE_CTRL_SHIFT 6 /* Bit offset to page control field. */ 677e32b20dSKelly Yancey 687e32b20dSKelly Yancey 697e32b20dSKelly Yancey /* Macros for working with mode pages. */ 707e32b20dSKelly Yancey #define MODE_PAGE_HEADER(mh) \ 717e32b20dSKelly Yancey (struct scsi_mode_page_header *)find_mode_page_6(mh) 727e32b20dSKelly Yancey 737e32b20dSKelly Yancey #define MODE_PAGE_DATA(mph) \ 747e32b20dSKelly Yancey (u_int8_t *)(mph) + sizeof(struct scsi_mode_page_header) 757e32b20dSKelly Yancey 767e32b20dSKelly Yancey 777e32b20dSKelly Yancey struct editentry { 787e32b20dSKelly Yancey STAILQ_ENTRY(editentry) link; 797e32b20dSKelly Yancey char *name; 807e32b20dSKelly Yancey char type; 817e32b20dSKelly Yancey int editable; 827e32b20dSKelly Yancey int size; 837e32b20dSKelly Yancey union { 847e32b20dSKelly Yancey int ivalue; 857e32b20dSKelly Yancey char *svalue; 867e32b20dSKelly Yancey } value; 877e32b20dSKelly Yancey }; 887e32b20dSKelly Yancey STAILQ_HEAD(, editentry) editlist; /* List of page entries. */ 897e32b20dSKelly Yancey int editlist_changed = 0; /* Whether any entries were changed. */ 907e32b20dSKelly Yancey 917e32b20dSKelly Yancey struct pagename { 927e32b20dSKelly Yancey SLIST_ENTRY(pagename) link; 937e32b20dSKelly Yancey int pagenum; 947e32b20dSKelly Yancey char *name; 957e32b20dSKelly Yancey }; 967e32b20dSKelly Yancey SLIST_HEAD(, pagename) namelist; /* Page number to name mappings. */ 977e32b20dSKelly Yancey 987e32b20dSKelly Yancey static char format[MAX_FORMAT_SPEC]; /* Buffer for scsi cdb format def. */ 997e32b20dSKelly Yancey 1007e32b20dSKelly Yancey static FILE *edit_file = NULL; /* File handle for edit file. */ 1017e32b20dSKelly Yancey static char edit_path[] = "/tmp/camXXXXXX"; 1027e32b20dSKelly Yancey 1037e32b20dSKelly Yancey 1047e32b20dSKelly Yancey /* Function prototypes. */ 1057e32b20dSKelly Yancey static void editentry_create(void *hook, int letter, void *arg, 1067e32b20dSKelly Yancey int count, char *name); 1077e32b20dSKelly Yancey static void editentry_update(void *hook, int letter, void *arg, 1087e32b20dSKelly Yancey int count, char *name); 1097e32b20dSKelly Yancey static int editentry_save(void *hook, char *name); 1107e32b20dSKelly Yancey static struct editentry *editentry_lookup(char *name); 1117e32b20dSKelly Yancey static int editentry_set(char *name, char *newvalue, 1127e32b20dSKelly Yancey int editonly); 1137e32b20dSKelly Yancey static void editlist_populate(struct cam_device *device, 1147e32b20dSKelly Yancey int modepage, int page_control, 1157e32b20dSKelly Yancey int dbd, int retries, int timeout); 1167e32b20dSKelly Yancey static void editlist_save(struct cam_device *device, int modepage, 1177e32b20dSKelly Yancey int page_control, int dbd, int retries, 1187e32b20dSKelly Yancey int timeout); 1197e32b20dSKelly Yancey static void nameentry_create(int pagenum, char *name); 1207e32b20dSKelly Yancey static struct pagename *nameentry_lookup(int pagenum); 1217e32b20dSKelly Yancey static int load_format(char *pagedb_path, int page); 1227e32b20dSKelly Yancey static int modepage_write(FILE *file, int editonly); 1237e32b20dSKelly Yancey static int modepage_read(FILE *file); 1247e32b20dSKelly Yancey static void modepage_edit(void); 1257e32b20dSKelly Yancey static void modepage_dump(struct cam_device *device, int page, 1267e32b20dSKelly Yancey int page_control, int dbd, int retries, 1277e32b20dSKelly Yancey int timeout); 1287e32b20dSKelly Yancey static void cleanup_editfile(void); 1297e32b20dSKelly Yancey void mode_edit(struct cam_device *device, int page, 1307e32b20dSKelly Yancey int page_control, int dbd, int edit, 1317e32b20dSKelly Yancey int binary, int retry_count, int timeout); 1327e32b20dSKelly Yancey void mode_list(struct cam_device *device, int page_control, 1337e32b20dSKelly Yancey int dbd, int retry_count, int timeout); 1347e32b20dSKelly Yancey 1357e32b20dSKelly Yancey 1367e32b20dSKelly Yancey #define returnerr(code) do { \ 1377e32b20dSKelly Yancey errno = code; \ 1387e32b20dSKelly Yancey return (-1); \ 1397e32b20dSKelly Yancey } while (0) 1407e32b20dSKelly Yancey 1417e32b20dSKelly Yancey 1427e32b20dSKelly Yancey #define RTRIM(string) do { \ 1433d438ad6SDavid E. O'Brien int _length; \ 1447e32b20dSKelly Yancey while (isspace(string[_length = strlen(string) - 1])) \ 1457e32b20dSKelly Yancey string[_length] = '\0'; \ 1467e32b20dSKelly Yancey } while (0) 1477e32b20dSKelly Yancey 1487e32b20dSKelly Yancey 1497e32b20dSKelly Yancey static void 1507e32b20dSKelly Yancey editentry_create(void *hook, int letter, void *arg, int count, char *name) 151525689f1SJustin T. Gibbs { 1527e32b20dSKelly Yancey struct editentry *newentry; /* Buffer to hold new entry. */ 153525689f1SJustin T. Gibbs 1547e32b20dSKelly Yancey /* Allocate memory for the new entry and a copy of the entry name. */ 1557e32b20dSKelly Yancey if ((newentry = malloc(sizeof(struct editentry))) == NULL || 1567e32b20dSKelly Yancey (newentry->name = strdup(name)) == NULL) 1577e32b20dSKelly Yancey err(EX_OSERR, NULL); 158525689f1SJustin T. Gibbs 1597e32b20dSKelly Yancey /* Trim any trailing whitespace for the entry name. */ 1607e32b20dSKelly Yancey RTRIM(newentry->name); 161525689f1SJustin T. Gibbs 1627e32b20dSKelly Yancey newentry->editable = (arg != NULL); 1637e32b20dSKelly Yancey newentry->type = letter; 1647e32b20dSKelly Yancey newentry->size = count; /* Placeholder; not accurate. */ 1657e32b20dSKelly Yancey newentry->value.svalue = NULL; 1667e32b20dSKelly Yancey 1677e32b20dSKelly Yancey STAILQ_INSERT_TAIL(&editlist, newentry, link); 168525689f1SJustin T. Gibbs } 169525689f1SJustin T. Gibbs 1707e32b20dSKelly Yancey static void 1717e32b20dSKelly Yancey editentry_update(void *hook, int letter, void *arg, int count, char *name) 172525689f1SJustin T. Gibbs { 1737e32b20dSKelly Yancey struct editentry *dest; /* Buffer to hold entry to update. */ 174525689f1SJustin T. Gibbs 1757e32b20dSKelly Yancey dest = editentry_lookup(name); 1767e32b20dSKelly Yancey assert(dest != NULL); 177525689f1SJustin T. Gibbs 1787e32b20dSKelly Yancey dest->type = letter; 1797e32b20dSKelly Yancey dest->size = count; /* We get the real size now. */ 180525689f1SJustin T. Gibbs 1817e32b20dSKelly Yancey switch (dest->type) { 1827e32b20dSKelly Yancey case 'i': /* Byte-sized integral type. */ 1837e32b20dSKelly Yancey case 'b': /* Bit-sized integral types. */ 1847e32b20dSKelly Yancey case 't': 1857e32b20dSKelly Yancey dest->value.ivalue = (intptr_t)arg; 186525689f1SJustin T. Gibbs break; 187525689f1SJustin T. Gibbs 1887e32b20dSKelly Yancey case 'c': /* Character array. */ 1897e32b20dSKelly Yancey case 'z': /* Null-padded string. */ 1907e32b20dSKelly Yancey editentry_set(name, (char *)arg, 0); 191525689f1SJustin T. Gibbs break; 192525689f1SJustin T. Gibbs default: 1937e32b20dSKelly Yancey /* NOTREACHED */ 194525689f1SJustin T. Gibbs } 195525689f1SJustin T. Gibbs } 196525689f1SJustin T. Gibbs 197525689f1SJustin T. Gibbs static int 1987e32b20dSKelly Yancey editentry_save(void *hook, char *name) 199525689f1SJustin T. Gibbs { 2007e32b20dSKelly Yancey struct editentry *src; /* Entry value to save. */ 201525689f1SJustin T. Gibbs 2027e32b20dSKelly Yancey src = editentry_lookup(name); 2037e32b20dSKelly Yancey assert(src != NULL); 204525689f1SJustin T. Gibbs 2057e32b20dSKelly Yancey switch (src->type) { 2067e32b20dSKelly Yancey case 'i': /* Byte-sized integral type. */ 2077e32b20dSKelly Yancey case 'b': /* Bit-sized integral types. */ 2087e32b20dSKelly Yancey case 't': 2097e32b20dSKelly Yancey return (src->value.ivalue); 2107e32b20dSKelly Yancey /* NOTREACHED */ 211525689f1SJustin T. Gibbs 2127e32b20dSKelly Yancey case 'c': /* Character array. */ 2137e32b20dSKelly Yancey case 'z': /* Null-padded string. */ 2147e32b20dSKelly Yancey return ((intptr_t)src->value.svalue); 2157e32b20dSKelly Yancey /* NOTREACHED */ 216525689f1SJustin T. Gibbs 2177e32b20dSKelly Yancey default: 2187e32b20dSKelly Yancey /* NOTREACHED */ 219525689f1SJustin T. Gibbs } 220525689f1SJustin T. Gibbs 2217e32b20dSKelly Yancey return (0); /* This should never happen. */ 2227e32b20dSKelly Yancey } 2237e32b20dSKelly Yancey 2247e32b20dSKelly Yancey static struct editentry * 2257e32b20dSKelly Yancey editentry_lookup(char *name) 2267e32b20dSKelly Yancey { 2277e32b20dSKelly Yancey struct editentry *scan; 2287e32b20dSKelly Yancey 2297e32b20dSKelly Yancey assert(name != NULL); 2307e32b20dSKelly Yancey 2317e32b20dSKelly Yancey STAILQ_FOREACH(scan, &editlist, link) { 2327e32b20dSKelly Yancey if (strcasecmp(scan->name, name) == 0) 2337e32b20dSKelly Yancey return (scan); 2347e32b20dSKelly Yancey } 2357e32b20dSKelly Yancey 2367e32b20dSKelly Yancey /* Not found during list traversal. */ 2377e32b20dSKelly Yancey return (NULL); 2387e32b20dSKelly Yancey } 2397e32b20dSKelly Yancey 2407e32b20dSKelly Yancey static int 2417e32b20dSKelly Yancey editentry_set(char *name, char *newvalue, int editonly) 2427e32b20dSKelly Yancey { 2437e32b20dSKelly Yancey struct editentry *dest; /* Modepage entry to update. */ 2447e32b20dSKelly Yancey char *cval; /* Pointer to new string value. */ 2457e32b20dSKelly Yancey char *convertend; /* End-of-conversion pointer. */ 2467e32b20dSKelly Yancey int ival; /* New integral value. */ 2477e32b20dSKelly Yancey int resolution; /* Resolution in bits for integer conversion. */ 2487e32b20dSKelly Yancey 2497e32b20dSKelly Yancey /* 2507e32b20dSKelly Yancey * Macro to determine the maximum value of the given size for the current 2517e32b20dSKelly Yancey * resolution. 2527e32b20dSKelly Yancey * XXX Lovely x86's optimize out the case of shifting by 32 and gcc doesn't 2537e32b20dSKelly Yancey * currently workaround it (even for int64's), so we have to kludge it. 2547e32b20dSKelly Yancey */ 2557e32b20dSKelly Yancey #define RESOLUTION_MAX(size) ((resolution * (size) == 32)? \ 256354a0adaSKelly Yancey 0xffffffff: (1 << (resolution * (size))) - 1) 2577e32b20dSKelly Yancey 2587e32b20dSKelly Yancey assert(newvalue != NULL); 2597e32b20dSKelly Yancey if (*newvalue == '\0') 2607e32b20dSKelly Yancey return (0); /* Nothing to do. */ 2617e32b20dSKelly Yancey 2627e32b20dSKelly Yancey if ((dest = editentry_lookup(name)) == NULL) 2637e32b20dSKelly Yancey returnerr(ENOENT); 2647e32b20dSKelly Yancey if (!dest->editable && editonly) 2657e32b20dSKelly Yancey returnerr(EPERM); 2667e32b20dSKelly Yancey 2677e32b20dSKelly Yancey switch (dest->type) { 2687e32b20dSKelly Yancey case 'i': /* Byte-sized integral type. */ 2697e32b20dSKelly Yancey case 'b': /* Bit-sized integral types. */ 2707e32b20dSKelly Yancey case 't': 2717e32b20dSKelly Yancey /* Convert the value string to an integer. */ 2727e32b20dSKelly Yancey resolution = (dest->type == 'i')? 8: 1; 2737e32b20dSKelly Yancey ival = (int)strtol(newvalue, &convertend, 0); 2747e32b20dSKelly Yancey if (*convertend != '\0') 2757e32b20dSKelly Yancey returnerr(EINVAL); 2767e32b20dSKelly Yancey if (ival > RESOLUTION_MAX(dest->size) || ival < 0) { 2777e32b20dSKelly Yancey int newival = (ival < 0)? 0: RESOLUTION_MAX(dest->size); 2787e32b20dSKelly Yancey warnx("value %d is out of range for entry %s; clipping " 2797e32b20dSKelly Yancey "to %d", ival, name, newival); 2807e32b20dSKelly Yancey ival = newival; 2817e32b20dSKelly Yancey } 2827e32b20dSKelly Yancey if (dest->value.ivalue != ival) 2837e32b20dSKelly Yancey editlist_changed = 1; 2847e32b20dSKelly Yancey dest->value.ivalue = ival; 2857e32b20dSKelly Yancey break; 2867e32b20dSKelly Yancey 2877e32b20dSKelly Yancey case 'c': /* Character array. */ 2887e32b20dSKelly Yancey case 'z': /* Null-padded string. */ 2897e32b20dSKelly Yancey if ((cval = malloc(dest->size + 1)) == NULL) 2907e32b20dSKelly Yancey err(EX_OSERR, NULL); 2917e32b20dSKelly Yancey bzero(cval, dest->size + 1); 2927e32b20dSKelly Yancey strncpy(cval, newvalue, dest->size); 2937e32b20dSKelly Yancey if (dest->type == 'z') { 2947e32b20dSKelly Yancey /* Convert trailing spaces to nulls. */ 2957e32b20dSKelly Yancey char *convertend; 2967e32b20dSKelly Yancey 2977e32b20dSKelly Yancey for (convertend = cval + dest->size; 2987e32b20dSKelly Yancey convertend >= cval; convertend--) { 2997e32b20dSKelly Yancey if (*convertend == ' ') 3007e32b20dSKelly Yancey *convertend = '\0'; 3017e32b20dSKelly Yancey else if (*convertend != '\0') 3027e32b20dSKelly Yancey break; 3037e32b20dSKelly Yancey } 3047e32b20dSKelly Yancey } 3057e32b20dSKelly Yancey if (strncmp(dest->value.svalue, cval, dest->size) == 0) { 3067e32b20dSKelly Yancey /* Nothing changed, free the newly allocated string. */ 3077e32b20dSKelly Yancey free(cval); 3087e32b20dSKelly Yancey break; 3097e32b20dSKelly Yancey } 3107e32b20dSKelly Yancey if (dest->value.svalue != NULL) { 3117e32b20dSKelly Yancey /* Free the current string buffer. */ 3127e32b20dSKelly Yancey free(dest->value.svalue); 3137e32b20dSKelly Yancey dest->value.svalue = NULL; 3147e32b20dSKelly Yancey } 3157e32b20dSKelly Yancey dest->value.svalue = cval; 3167e32b20dSKelly Yancey editlist_changed = 1; 3177e32b20dSKelly Yancey break; 3187e32b20dSKelly Yancey 3197e32b20dSKelly Yancey default: 3207e32b20dSKelly Yancey /* NOTREACHED */ 3217e32b20dSKelly Yancey } 3227e32b20dSKelly Yancey 3237e32b20dSKelly Yancey return (0); 3247e32b20dSKelly Yancey #undef RESOLUTION_MAX 325525689f1SJustin T. Gibbs } 326525689f1SJustin T. Gibbs 327525689f1SJustin T. Gibbs static void 3287e32b20dSKelly Yancey nameentry_create(int pagenum, char *name) { 3297e32b20dSKelly Yancey struct pagename *newentry; 3307e32b20dSKelly Yancey 3317e32b20dSKelly Yancey if (pagenum < 0 || name == NULL || name[0] == '\0') 3327e32b20dSKelly Yancey return; 3337e32b20dSKelly Yancey 3347e32b20dSKelly Yancey /* Allocate memory for the new entry and a copy of the entry name. */ 3357e32b20dSKelly Yancey if ((newentry = malloc(sizeof(struct pagename))) == NULL || 3367e32b20dSKelly Yancey (newentry->name = strdup(name)) == NULL) 3377e32b20dSKelly Yancey err(EX_OSERR, NULL); 3387e32b20dSKelly Yancey 3397e32b20dSKelly Yancey /* Trim any trailing whitespace for the page name. */ 3407e32b20dSKelly Yancey RTRIM(newentry->name); 3417e32b20dSKelly Yancey 3427e32b20dSKelly Yancey newentry->pagenum = pagenum; 3437e32b20dSKelly Yancey SLIST_INSERT_HEAD(&namelist, newentry, link); 3447e32b20dSKelly Yancey } 3457e32b20dSKelly Yancey 3467e32b20dSKelly Yancey static struct pagename * 3477e32b20dSKelly Yancey nameentry_lookup(int pagenum) { 3487e32b20dSKelly Yancey struct pagename *scan; 3497e32b20dSKelly Yancey 3507e32b20dSKelly Yancey SLIST_FOREACH(scan, &namelist, link) { 3517e32b20dSKelly Yancey if (pagenum == scan->pagenum) 3527e32b20dSKelly Yancey return (scan); 3537e32b20dSKelly Yancey } 3547e32b20dSKelly Yancey 3557e32b20dSKelly Yancey /* Not found during list traversal. */ 3567e32b20dSKelly Yancey return (NULL); 3577e32b20dSKelly Yancey } 3587e32b20dSKelly Yancey 3597e32b20dSKelly Yancey static int 3607e32b20dSKelly Yancey load_format(char *pagedb_path, int page) 361525689f1SJustin T. Gibbs { 3627e32b20dSKelly Yancey FILE *pagedb; 3637e32b20dSKelly Yancey char str_pagenum[MAX_PAGENUM_LEN]; 3647e32b20dSKelly Yancey char str_pagename[MAX_PAGENAME_LEN]; 3657e32b20dSKelly Yancey int pagenum; 3667e32b20dSKelly Yancey int depth; /* Quoting depth. */ 3677e32b20dSKelly Yancey int found; 3687e32b20dSKelly Yancey int lineno; 3697e32b20dSKelly Yancey enum { LOCATE, PAGENAME, PAGEDEF } state; 3707e32b20dSKelly Yancey char c; 3717e32b20dSKelly Yancey 3727e32b20dSKelly Yancey #define SETSTATE_LOCATE do { \ 3737e32b20dSKelly Yancey str_pagenum[0] = '\0'; \ 3747e32b20dSKelly Yancey str_pagename[0] = '\0'; \ 3757e32b20dSKelly Yancey pagenum = -1; \ 3767e32b20dSKelly Yancey state = LOCATE; \ 3777e32b20dSKelly Yancey } while (0) 3787e32b20dSKelly Yancey 3797e32b20dSKelly Yancey #define SETSTATE_PAGENAME do { \ 3807e32b20dSKelly Yancey str_pagename[0] = '\0'; \ 3817e32b20dSKelly Yancey state = PAGENAME; \ 3827e32b20dSKelly Yancey } while (0) 3837e32b20dSKelly Yancey 3847e32b20dSKelly Yancey #define SETSTATE_PAGEDEF do { \ 3857e32b20dSKelly Yancey format[0] = '\0'; \ 3867e32b20dSKelly Yancey state = PAGEDEF; \ 3877e32b20dSKelly Yancey } while (0) 3887e32b20dSKelly Yancey 3897e32b20dSKelly Yancey #define UPDATE_LINENO do { \ 3907e32b20dSKelly Yancey if (c == '\n') \ 3917e32b20dSKelly Yancey lineno++; \ 3927e32b20dSKelly Yancey } while (0) 3937e32b20dSKelly Yancey 3947e32b20dSKelly Yancey #define BUFFERFULL(buffer) (strlen(buffer) + 1 >= sizeof(buffer)) 3957e32b20dSKelly Yancey 3967e32b20dSKelly Yancey if ((pagedb = fopen(pagedb_path, "r")) == NULL) 3977e32b20dSKelly Yancey returnerr(ENOENT); 3987e32b20dSKelly Yancey 3997e32b20dSKelly Yancey SLIST_INIT(&namelist); 4007e32b20dSKelly Yancey 4017e32b20dSKelly Yancey depth = 0; 4027e32b20dSKelly Yancey lineno = 0; 4037e32b20dSKelly Yancey found = 0; 4047e32b20dSKelly Yancey SETSTATE_LOCATE; 4057e32b20dSKelly Yancey while ((c = fgetc(pagedb)) != EOF) { 4067e32b20dSKelly Yancey 4077e32b20dSKelly Yancey /* Keep a line count to make error messages more useful. */ 4087e32b20dSKelly Yancey UPDATE_LINENO; 4097e32b20dSKelly Yancey 4107e32b20dSKelly Yancey /* Skip over comments anywhere in the mode database. */ 4117e32b20dSKelly Yancey if (c == '#') { 4127e32b20dSKelly Yancey do { 4137e32b20dSKelly Yancey c = fgetc(pagedb); 4147e32b20dSKelly Yancey } while (c != '\n' && c != EOF); 4157e32b20dSKelly Yancey UPDATE_LINENO; 4167e32b20dSKelly Yancey continue; 4177e32b20dSKelly Yancey } 4187e32b20dSKelly Yancey 4197e32b20dSKelly Yancey /* Strip out newline characters. */ 4207e32b20dSKelly Yancey if (c == '\n') 4217e32b20dSKelly Yancey continue; 4227e32b20dSKelly Yancey 4237e32b20dSKelly Yancey /* Keep track of the nesting depth for braces. */ 4247e32b20dSKelly Yancey if (c == PAGEDEF_START) 4257e32b20dSKelly Yancey depth++; 4267e32b20dSKelly Yancey else if (c == PAGEDEF_END) { 4277e32b20dSKelly Yancey depth--; 4287e32b20dSKelly Yancey if (depth < 0) { 4297e32b20dSKelly Yancey errx(EX_OSFILE, "%s:%d: %s", pagedb_path, 4307e32b20dSKelly Yancey lineno, "mismatched bracket"); 4317e32b20dSKelly Yancey } 4327e32b20dSKelly Yancey } 4337e32b20dSKelly Yancey 4347e32b20dSKelly Yancey switch (state) { 4357e32b20dSKelly Yancey case LOCATE: 4367e32b20dSKelly Yancey /* 4377e32b20dSKelly Yancey * Locate the page the user is interested in, skipping 4387e32b20dSKelly Yancey * all others. 4397e32b20dSKelly Yancey */ 4407e32b20dSKelly Yancey if (isspace(c)) { 4417e32b20dSKelly Yancey /* Ignore all whitespace between pages. */ 4427e32b20dSKelly Yancey break; 4437e32b20dSKelly Yancey } else if (depth == 0 && c == PAGEENTRY_END) { 4447e32b20dSKelly Yancey /* 4457e32b20dSKelly Yancey * A page entry terminator will reset page 4467e32b20dSKelly Yancey * scanning (useful for assigning names to 4477e32b20dSKelly Yancey * modes without providing a mode definition). 4487e32b20dSKelly Yancey */ 4497e32b20dSKelly Yancey /* Record the name of this page. */ 4507e32b20dSKelly Yancey pagenum = strtol(str_pagenum, NULL, 0); 4517e32b20dSKelly Yancey nameentry_create(pagenum, str_pagename); 4527e32b20dSKelly Yancey SETSTATE_LOCATE; 4537e32b20dSKelly Yancey } else if (depth == 0 && c == PAGENAME_START) { 4547e32b20dSKelly Yancey SETSTATE_PAGENAME; 4557e32b20dSKelly Yancey } else if (c == PAGEDEF_START) { 4567e32b20dSKelly Yancey pagenum = strtol(str_pagenum, NULL, 0); 4577e32b20dSKelly Yancey if (depth == 1) { 4587e32b20dSKelly Yancey /* Record the name of this page. */ 4597e32b20dSKelly Yancey nameentry_create(pagenum, str_pagename); 4607e32b20dSKelly Yancey /* 4617e32b20dSKelly Yancey * Only record the format if this is 4627e32b20dSKelly Yancey * the page we are interested in. 4637e32b20dSKelly Yancey */ 4647e32b20dSKelly Yancey if (page == pagenum && !found) 4657e32b20dSKelly Yancey SETSTATE_PAGEDEF; 4667e32b20dSKelly Yancey } 4677e32b20dSKelly Yancey } else if (c == PAGEDEF_END) { 4687e32b20dSKelly Yancey /* Reset the processor state. */ 4697e32b20dSKelly Yancey SETSTATE_LOCATE; 4707e32b20dSKelly Yancey } else if (depth == 0 && ! BUFFERFULL(str_pagenum)) { 4717e32b20dSKelly Yancey strncat(str_pagenum, &c, 1); 4727e32b20dSKelly Yancey } else if (depth == 0) { 4737e32b20dSKelly Yancey errx(EX_OSFILE, "%s:%d: %s %d %s", pagedb_path, 4747e32b20dSKelly Yancey lineno, "page identifier exceeds", 4757e32b20dSKelly Yancey sizeof(str_pagenum) - 1, "characters"); 4767e32b20dSKelly Yancey } 4777e32b20dSKelly Yancey break; 4787e32b20dSKelly Yancey 4797e32b20dSKelly Yancey case PAGENAME: 4807e32b20dSKelly Yancey if (c == PAGENAME_END) { 4817e32b20dSKelly Yancey /* 4827e32b20dSKelly Yancey * Return to LOCATE state without resetting the 4837e32b20dSKelly Yancey * page number buffer. 4847e32b20dSKelly Yancey */ 4857e32b20dSKelly Yancey state = LOCATE; 4867e32b20dSKelly Yancey } else if (! BUFFERFULL(str_pagename)) { 4877e32b20dSKelly Yancey strncat(str_pagename, &c, 1); 4887e32b20dSKelly Yancey } else { 4897e32b20dSKelly Yancey errx(EX_OSFILE, "%s:%d: %s %d %s", pagedb_path, 4907e32b20dSKelly Yancey lineno, "page name exceeds", 4917e32b20dSKelly Yancey sizeof(str_pagenum) - 1, "characters"); 4927e32b20dSKelly Yancey } 4937e32b20dSKelly Yancey break; 4947e32b20dSKelly Yancey 4957e32b20dSKelly Yancey case PAGEDEF: 4967e32b20dSKelly Yancey /* 4977e32b20dSKelly Yancey * Transfer the page definition into a format buffer 4987e32b20dSKelly Yancey * suitable for use with CDB encoding/decoding routines. 4997e32b20dSKelly Yancey */ 5007e32b20dSKelly Yancey if (depth == 0) { 5017e32b20dSKelly Yancey found = 1; 5027e32b20dSKelly Yancey SETSTATE_LOCATE; 5037e32b20dSKelly Yancey } else if (! BUFFERFULL(format)) { 5047e32b20dSKelly Yancey strncat(format, &c, 1); 5057e32b20dSKelly Yancey } else { 5067e32b20dSKelly Yancey errx(EX_OSFILE, "%s:%d: %s %d %s", pagedb_path, 5077e32b20dSKelly Yancey lineno, "page definition exceeds", 5087e32b20dSKelly Yancey sizeof(format) - 1, "characters"); 5097e32b20dSKelly Yancey } 5107e32b20dSKelly Yancey break; 5117e32b20dSKelly Yancey 5127e32b20dSKelly Yancey default: 5137e32b20dSKelly Yancey /* NOTREACHED */ 5147e32b20dSKelly Yancey } 5157e32b20dSKelly Yancey 5167e32b20dSKelly Yancey /* Repeat processing loop with next character. */ 5177e32b20dSKelly Yancey } 5187e32b20dSKelly Yancey 5197e32b20dSKelly Yancey if (ferror(pagedb)) 5207e32b20dSKelly Yancey err(EX_OSFILE, "%s", pagedb_path); 5217e32b20dSKelly Yancey 5227e32b20dSKelly Yancey /* Close the SCSI page database. */ 5237e32b20dSKelly Yancey fclose(pagedb); 5247e32b20dSKelly Yancey 5257e32b20dSKelly Yancey if (!found) /* Never found a matching page. */ 5267e32b20dSKelly Yancey returnerr(ESRCH); 5277e32b20dSKelly Yancey 5287e32b20dSKelly Yancey return (0); 5297e32b20dSKelly Yancey } 5307e32b20dSKelly Yancey 5317e32b20dSKelly Yancey static void 5327e32b20dSKelly Yancey editlist_populate(struct cam_device *device, int modepage, int page_control, 5337e32b20dSKelly Yancey int dbd, int retries, int timeout) 5347e32b20dSKelly Yancey { 5357e32b20dSKelly Yancey u_int8_t data[MAX_COMMAND_SIZE];/* Buffer to hold sense data. */ 5367e32b20dSKelly Yancey u_int8_t *mode_pars; /* Pointer to modepage params. */ 5377e32b20dSKelly Yancey struct scsi_mode_header_6 *mh; /* Location of mode header. */ 5387e32b20dSKelly Yancey struct scsi_mode_page_header *mph; 5397e32b20dSKelly Yancey 5407e32b20dSKelly Yancey STAILQ_INIT(&editlist); 5417e32b20dSKelly Yancey 5427e32b20dSKelly Yancey /* Fetch changeable values; use to build initial editlist. */ 5437e32b20dSKelly Yancey mode_sense(device, modepage, 1, dbd, retries, timeout, data, 5447e32b20dSKelly Yancey sizeof(data)); 5457e32b20dSKelly Yancey 5467e32b20dSKelly Yancey mh = (struct scsi_mode_header_6 *)data; 5477e32b20dSKelly Yancey mph = MODE_PAGE_HEADER(mh); 5487e32b20dSKelly Yancey mode_pars = MODE_PAGE_DATA(mph); 5497e32b20dSKelly Yancey 5507e32b20dSKelly Yancey /* Decode the value data, creating edit_entries for each value. */ 5517e32b20dSKelly Yancey buff_decode_visit(mode_pars, mh->data_length, format, 5527e32b20dSKelly Yancey editentry_create, 0); 5537e32b20dSKelly Yancey 5547e32b20dSKelly Yancey /* Fetch the current/saved values; use to set editentry values. */ 5557e32b20dSKelly Yancey mode_sense(device, modepage, page_control, dbd, retries, timeout, data, 5567e32b20dSKelly Yancey sizeof(data)); 5577e32b20dSKelly Yancey buff_decode_visit(mode_pars, mh->data_length, format, 5587e32b20dSKelly Yancey editentry_update, 0); 5597e32b20dSKelly Yancey } 5607e32b20dSKelly Yancey 5617e32b20dSKelly Yancey static void 5627e32b20dSKelly Yancey editlist_save(struct cam_device *device, int modepage, int page_control, 5637e32b20dSKelly Yancey int dbd, int retries, int timeout) 5647e32b20dSKelly Yancey { 5657e32b20dSKelly Yancey u_int8_t data[MAX_COMMAND_SIZE];/* Buffer to hold sense data. */ 5667e32b20dSKelly Yancey u_int8_t *mode_pars; /* Pointer to modepage params. */ 5677e32b20dSKelly Yancey struct scsi_mode_header_6 *mh; /* Location of mode header. */ 5687e32b20dSKelly Yancey struct scsi_mode_page_header *mph; 5697e32b20dSKelly Yancey 5707e32b20dSKelly Yancey /* Make sure that something changed before continuing. */ 5717e32b20dSKelly Yancey if (! editlist_changed) 5727e32b20dSKelly Yancey return; 5737e32b20dSKelly Yancey 5747e32b20dSKelly Yancey /* 5757e32b20dSKelly Yancey * Preload the CDB buffer with the current mode page data. 5767e32b20dSKelly Yancey * XXX If buff_encode_visit would return the number of bytes encoded 5777e32b20dSKelly Yancey * we *should* use that to build a header from scratch. As it is 5787e32b20dSKelly Yancey * now, we need mode_sense to find out the page length. 5797e32b20dSKelly Yancey */ 5807e32b20dSKelly Yancey mode_sense(device, modepage, page_control, dbd, retries, timeout, data, 5817e32b20dSKelly Yancey sizeof(data)); 5827e32b20dSKelly Yancey 5837e32b20dSKelly Yancey /* Initial headers & offsets. */ 5847e32b20dSKelly Yancey mh = (struct scsi_mode_header_6 *)data; 5857e32b20dSKelly Yancey mph = MODE_PAGE_HEADER(mh); 5867e32b20dSKelly Yancey mode_pars = MODE_PAGE_DATA(mph); 5877e32b20dSKelly Yancey 5887e32b20dSKelly Yancey /* Encode the value data to be passed back to the device. */ 58934f71eebSJohn Baldwin buff_encode_visit(mode_pars, mh->data_length, format, 5907e32b20dSKelly Yancey editentry_save, 0); 5917e32b20dSKelly Yancey 5927e32b20dSKelly Yancey /* Eliminate block descriptors. */ 5937e32b20dSKelly Yancey bcopy(mph, ((u_int8_t *)mh) + sizeof(*mh), 5947e32b20dSKelly Yancey sizeof(*mph) + mph->page_length); 5957e32b20dSKelly Yancey 5967e32b20dSKelly Yancey /* Recalculate headers & offsets. */ 5977e32b20dSKelly Yancey mh->blk_desc_len = 0; /* No block descriptors. */ 5987e32b20dSKelly Yancey mh->dev_spec = 0; /* Clear device-specific parameters. */ 5997e32b20dSKelly Yancey mph = MODE_PAGE_HEADER(mh); 6007e32b20dSKelly Yancey mode_pars = MODE_PAGE_DATA(mph); 6017e32b20dSKelly Yancey 6027e32b20dSKelly Yancey mph->page_code &= SMS_PAGE_CODE;/* Isolate just the page code. */ 6037e32b20dSKelly Yancey mh->data_length = 0; /* Reserved for MODE SELECT command. */ 6047e32b20dSKelly Yancey 6057e32b20dSKelly Yancey /* 6067e32b20dSKelly Yancey * Write the changes back to the device. If the user editted control 6077e32b20dSKelly Yancey * page 3 (saved values) then request the changes be permanently 6087e32b20dSKelly Yancey * recorded. 6097e32b20dSKelly Yancey */ 610354a0adaSKelly Yancey mode_select(device, 611354a0adaSKelly Yancey (page_control << PAGE_CTRL_SHIFT == SMS_PAGE_CTRL_SAVED), 612354a0adaSKelly Yancey retries, timeout, (u_int8_t *)mh, 613354a0adaSKelly Yancey sizeof(*mh) + mh->blk_desc_len + sizeof(*mph) + mph->page_length); 6147e32b20dSKelly Yancey } 6157e32b20dSKelly Yancey 6167e32b20dSKelly Yancey static int 6177e32b20dSKelly Yancey modepage_write(FILE *file, int editonly) 6187e32b20dSKelly Yancey { 6197e32b20dSKelly Yancey struct editentry *scan; 6207e32b20dSKelly Yancey int written = 0; 6217e32b20dSKelly Yancey 6227e32b20dSKelly Yancey STAILQ_FOREACH(scan, &editlist, link) { 6237e32b20dSKelly Yancey if (scan->editable || !editonly) { 6247e32b20dSKelly Yancey written++; 6257e32b20dSKelly Yancey if (scan->type == 'c' || scan->type == 'z') { 6267e32b20dSKelly Yancey fprintf(file, "%s: %s\n", scan->name, 6277e32b20dSKelly Yancey scan->value.svalue); 6287e32b20dSKelly Yancey } else { 6297e32b20dSKelly Yancey fprintf(file, "%s: %d\n", scan->name, 6307e32b20dSKelly Yancey scan->value.ivalue); 6317e32b20dSKelly Yancey } 6327e32b20dSKelly Yancey } 6337e32b20dSKelly Yancey } 6347e32b20dSKelly Yancey return (written); 6357e32b20dSKelly Yancey } 6367e32b20dSKelly Yancey 6377e32b20dSKelly Yancey static int 6387e32b20dSKelly Yancey modepage_read(FILE *file) 6397e32b20dSKelly Yancey { 6407e32b20dSKelly Yancey char *buffer; /* Pointer to dynamic line buffer. */ 6417e32b20dSKelly Yancey char *line; /* Pointer to static fgetln buffer. */ 6427e32b20dSKelly Yancey char *name; /* Name portion of the line buffer. */ 6437e32b20dSKelly Yancey char *value; /* Value portion of line buffer. */ 6447e32b20dSKelly Yancey int length; /* Length of static fgetln buffer. */ 6457e32b20dSKelly Yancey 6467e32b20dSKelly Yancey #define ABORT_READ(message, param) do { \ 6477e32b20dSKelly Yancey warnx(message, param); \ 6487e32b20dSKelly Yancey free(buffer); \ 6497e32b20dSKelly Yancey returnerr(EAGAIN); \ 6507e32b20dSKelly Yancey } while (0) 6517e32b20dSKelly Yancey 6527e32b20dSKelly Yancey while ((line = fgetln(file, &length)) != NULL) { 6537e32b20dSKelly Yancey /* Trim trailing whitespace (including optional newline). */ 6547e32b20dSKelly Yancey while (length > 0 && isspace(line[length - 1])) 6557e32b20dSKelly Yancey length--; 6567e32b20dSKelly Yancey 6577e32b20dSKelly Yancey /* Allocate a buffer to hold the line + terminating null. */ 6587e32b20dSKelly Yancey if ((buffer = malloc(length + 1)) == NULL) 6597e32b20dSKelly Yancey err(EX_OSERR, NULL); 6607e32b20dSKelly Yancey memcpy(buffer, line, length); 6617e32b20dSKelly Yancey buffer[length] = '\0'; 6627e32b20dSKelly Yancey 6637e32b20dSKelly Yancey /* Strip out comments. */ 6647e32b20dSKelly Yancey if ((value = strchr(buffer, '#')) != NULL) 6657e32b20dSKelly Yancey *value = '\0'; 6667e32b20dSKelly Yancey 6677e32b20dSKelly Yancey /* The name is first in the buffer. Trim whitespace.*/ 6687e32b20dSKelly Yancey name = buffer; 6697e32b20dSKelly Yancey RTRIM(name); 6707e32b20dSKelly Yancey while (isspace(*name)) 6717e32b20dSKelly Yancey name++; 6727e32b20dSKelly Yancey 6737e32b20dSKelly Yancey /* Skip empty lines. */ 6747e32b20dSKelly Yancey if (strlen(name) == 0) 6757e32b20dSKelly Yancey continue; 6767e32b20dSKelly Yancey 6777e32b20dSKelly Yancey /* The name ends at the colon; the value starts there. */ 6787e32b20dSKelly Yancey if ((value = strrchr(buffer, ':')) == NULL) 6797e32b20dSKelly Yancey ABORT_READ("no value associated with %s", name); 6807e32b20dSKelly Yancey *value = '\0'; /* Null-terminate name. */ 6817e32b20dSKelly Yancey value++; /* Value starts afterwards. */ 6827e32b20dSKelly Yancey 6837e32b20dSKelly Yancey /* Trim leading and trailing whitespace. */ 6847e32b20dSKelly Yancey RTRIM(value); 6857e32b20dSKelly Yancey while (isspace(*value)) 6867e32b20dSKelly Yancey value++; 6877e32b20dSKelly Yancey 6887e32b20dSKelly Yancey /* Make sure there is a value left. */ 6897e32b20dSKelly Yancey if (strlen(value) == 0) 6907e32b20dSKelly Yancey ABORT_READ("no value associated with %s", name); 6917e32b20dSKelly Yancey 6927e32b20dSKelly Yancey /* Update our in-memory copy of the modepage entry value. */ 6937e32b20dSKelly Yancey if (editentry_set(name, value, 1) != 0) { 6947e32b20dSKelly Yancey if (errno == ENOENT) { 6957e32b20dSKelly Yancey /* No entry by the name. */ 6967e32b20dSKelly Yancey ABORT_READ("no such modepage entry \"%s\"", 6977e32b20dSKelly Yancey name); 6987e32b20dSKelly Yancey } else if (errno == EINVAL) { 6997e32b20dSKelly Yancey /* Invalid value. */ 7007e32b20dSKelly Yancey ABORT_READ("Invalid value for entry \"%s\"", 7017e32b20dSKelly Yancey name); 7027e32b20dSKelly Yancey } else if (errno == ERANGE) { 7037e32b20dSKelly Yancey /* Value out of range for entry type. */ 7047e32b20dSKelly Yancey ABORT_READ("value out of range for %s", name); 7057e32b20dSKelly Yancey } else if (errno == EPERM) { 7067e32b20dSKelly Yancey /* Entry is not editable; not fatal. */ 7077e32b20dSKelly Yancey warnx("modepage entry \"%s\" is read-only; " 7087e32b20dSKelly Yancey "skipping.", name); 7097e32b20dSKelly Yancey } 7107e32b20dSKelly Yancey } 7117e32b20dSKelly Yancey 7127e32b20dSKelly Yancey free(buffer); 7137e32b20dSKelly Yancey } 7147e32b20dSKelly Yancey return (ferror(file)? -1: 0); 7157e32b20dSKelly Yancey 7167e32b20dSKelly Yancey #undef ABORT_READ 7177e32b20dSKelly Yancey } 7187e32b20dSKelly Yancey 7197e32b20dSKelly Yancey static void 7207e32b20dSKelly Yancey modepage_edit(void) 7217e32b20dSKelly Yancey { 7227e32b20dSKelly Yancey char *editor; 7237e32b20dSKelly Yancey char *commandline; 7247e32b20dSKelly Yancey int fd; 7257e32b20dSKelly Yancey int written; 7267e32b20dSKelly Yancey 7277e32b20dSKelly Yancey if (!isatty(fileno(stdin))) { 7287e32b20dSKelly Yancey /* Not a tty, read changes from stdin. */ 7297e32b20dSKelly Yancey modepage_read(stdin); 7307e32b20dSKelly Yancey return; 7317e32b20dSKelly Yancey } 7327e32b20dSKelly Yancey 7337e32b20dSKelly Yancey /* Lookup editor to invoke. */ 7347e32b20dSKelly Yancey if ((editor = getenv("EDITOR")) == NULL) 7357e32b20dSKelly Yancey editor = DEFAULT_EDITOR; 7367e32b20dSKelly Yancey 7377e32b20dSKelly Yancey /* Create temp file for editor to modify. */ 7387e32b20dSKelly Yancey if ((fd = mkstemp(edit_path)) == -1) 7397e32b20dSKelly Yancey errx(EX_CANTCREAT, "mkstemp failed"); 7407e32b20dSKelly Yancey 7417e32b20dSKelly Yancey atexit(cleanup_editfile); 7427e32b20dSKelly Yancey 7437e32b20dSKelly Yancey if ((edit_file = fdopen(fd, "w")) == NULL) 7447e32b20dSKelly Yancey err(EX_NOINPUT, "%s", edit_path); 7457e32b20dSKelly Yancey 7467e32b20dSKelly Yancey written = modepage_write(edit_file, 1); 747525689f1SJustin T. Gibbs 748525689f1SJustin T. Gibbs fclose(edit_file); 7497e32b20dSKelly Yancey edit_file = NULL; 750525689f1SJustin T. Gibbs 7517e32b20dSKelly Yancey if (written == 0) { 7527e32b20dSKelly Yancey warnx("no editable entries"); 7537e32b20dSKelly Yancey cleanup_editfile(); 7547e32b20dSKelly Yancey return; 7557e32b20dSKelly Yancey } 756525689f1SJustin T. Gibbs 7577e32b20dSKelly Yancey /* 7587e32b20dSKelly Yancey * Allocate memory to hold the command line (the 2 extra characters 7597e32b20dSKelly Yancey * are to hold the argument separator (a space), and the terminating 7607e32b20dSKelly Yancey * null character. 7617e32b20dSKelly Yancey */ 7627e32b20dSKelly Yancey commandline = malloc(strlen(editor) + strlen(edit_path) + 2); 7637e32b20dSKelly Yancey if (commandline == NULL) 7647e32b20dSKelly Yancey err(EX_OSERR, NULL); 7657e32b20dSKelly Yancey sprintf(commandline, "%s %s", editor, edit_path); 7667e32b20dSKelly Yancey 7677e32b20dSKelly Yancey /* Invoke the editor on the temp file. */ 7687e32b20dSKelly Yancey if (system(commandline) == -1) 7697e32b20dSKelly Yancey err(EX_UNAVAILABLE, "could not invoke %s", editor); 7707e32b20dSKelly Yancey free(commandline); 7717e32b20dSKelly Yancey 7727e32b20dSKelly Yancey if ((edit_file = fopen(edit_path, "r")) == NULL) 7737e32b20dSKelly Yancey err(EX_NOINPUT, "%s", edit_path); 7747e32b20dSKelly Yancey 7757e32b20dSKelly Yancey /* Read any changes made to the temp file. */ 7767e32b20dSKelly Yancey modepage_read(edit_file); 7777e32b20dSKelly Yancey 7787e32b20dSKelly Yancey cleanup_editfile(); 7797e32b20dSKelly Yancey } 7807e32b20dSKelly Yancey 7817e32b20dSKelly Yancey static void 7827e32b20dSKelly Yancey modepage_dump(struct cam_device *device, int page, int page_control, int dbd, 7837e32b20dSKelly Yancey int retries, int timeout) 7847e32b20dSKelly Yancey { 7857e32b20dSKelly Yancey u_int8_t data[MAX_COMMAND_SIZE];/* Buffer to hold sense data. */ 7867e32b20dSKelly Yancey u_int8_t *mode_pars; /* Pointer to modepage params. */ 7877e32b20dSKelly Yancey struct scsi_mode_header_6 *mh; /* Location of mode header. */ 7887e32b20dSKelly Yancey struct scsi_mode_page_header *mph; 7897e32b20dSKelly Yancey int index; /* Index for scanning mode params. */ 7907e32b20dSKelly Yancey 7917e32b20dSKelly Yancey mode_sense(device, page, page_control, dbd, retries, timeout, data, 7927e32b20dSKelly Yancey sizeof(data)); 7937e32b20dSKelly Yancey 7947e32b20dSKelly Yancey mh = (struct scsi_mode_header_6 *)data; 7957e32b20dSKelly Yancey mph = MODE_PAGE_HEADER(mh); 7967e32b20dSKelly Yancey mode_pars = MODE_PAGE_DATA(mph); 7977e32b20dSKelly Yancey 7987e32b20dSKelly Yancey /* Print the raw mode page data with newlines each 8 bytes. */ 7997e32b20dSKelly Yancey for (index = 0; index < mph->page_length; index++) { 8007e32b20dSKelly Yancey printf("%02x%c",mode_pars[index], 8017e32b20dSKelly Yancey (((index + 1) % 8) == 0) ? '\n' : ' '); 8027e32b20dSKelly Yancey } 8037e32b20dSKelly Yancey putchar('\n'); 8047e32b20dSKelly Yancey } 8057e32b20dSKelly Yancey 8067e32b20dSKelly Yancey static void 8077e32b20dSKelly Yancey cleanup_editfile(void) 8087e32b20dSKelly Yancey { 8097e32b20dSKelly Yancey if (edit_file == NULL) 8107e32b20dSKelly Yancey return; 8117e32b20dSKelly Yancey if (fclose(edit_file) != 0 || unlink(edit_path) != 0) 8127e32b20dSKelly Yancey warn("%s", edit_path); 8137e32b20dSKelly Yancey edit_file = NULL; 814525689f1SJustin T. Gibbs } 815525689f1SJustin T. Gibbs 816525689f1SJustin T. Gibbs void 817525689f1SJustin T. Gibbs mode_edit(struct cam_device *device, int page, int page_control, int dbd, 8187e32b20dSKelly Yancey int edit, int binary, int retry_count, int timeout) 819525689f1SJustin T. Gibbs { 8207e32b20dSKelly Yancey char *pagedb_path; /* Path to modepage database. */ 821525689f1SJustin T. Gibbs 8227e32b20dSKelly Yancey if (edit && binary) 8237e32b20dSKelly Yancey errx(EX_USAGE, "cannot edit in binary mode."); 824525689f1SJustin T. Gibbs 8257e32b20dSKelly Yancey if (! binary) { 8267e32b20dSKelly Yancey if ((pagedb_path = getenv("SCSI_MODES")) == NULL) 8277e32b20dSKelly Yancey pagedb_path = DEFAULT_SCSI_MODE_DB; 828525689f1SJustin T. Gibbs 8297e32b20dSKelly Yancey if (load_format(pagedb_path, page) != 0 && (edit || verbose)) { 8307e32b20dSKelly Yancey if (errno == ENOENT) { 8317e32b20dSKelly Yancey /* Modepage database file not found. */ 8327e32b20dSKelly Yancey warn("cannot open modepage database \"%s\"", 8337e32b20dSKelly Yancey pagedb_path); 8347e32b20dSKelly Yancey } else if (errno == ESRCH) { 8357e32b20dSKelly Yancey /* Modepage entry not found in database. */ 8367e32b20dSKelly Yancey warnx("modepage %d not found in database" 8377e32b20dSKelly Yancey "\"%s\"", page, pagedb_path); 8387e32b20dSKelly Yancey } 8397e32b20dSKelly Yancey /* We can recover in display mode, otherwise we exit. */ 8407e32b20dSKelly Yancey if (!edit) { 8417e32b20dSKelly Yancey warnx("reverting to binary display only"); 8427e32b20dSKelly Yancey binary = 1; 8437e32b20dSKelly Yancey } else 8447e32b20dSKelly Yancey exit(EX_OSFILE); 8457e32b20dSKelly Yancey } 8467e32b20dSKelly Yancey 8477e32b20dSKelly Yancey editlist_populate(device, page, page_control, dbd, retry_count, 8487e32b20dSKelly Yancey timeout); 849525689f1SJustin T. Gibbs } 850525689f1SJustin T. Gibbs 851525689f1SJustin T. Gibbs if (edit) { 852354a0adaSKelly Yancey if (page_control << PAGE_CTRL_SHIFT != SMS_PAGE_CTRL_CURRENT && 853354a0adaSKelly Yancey page_control << PAGE_CTRL_SHIFT != SMS_PAGE_CTRL_SAVED) 8547e32b20dSKelly Yancey errx(EX_USAGE, "it only makes sense to edit page 0 " 855525689f1SJustin T. Gibbs "(current) or page 3 (saved values)"); 8567e32b20dSKelly Yancey modepage_edit(); 8577e32b20dSKelly Yancey editlist_save(device, page, page_control, dbd, retry_count, 8587e32b20dSKelly Yancey timeout); 8597e32b20dSKelly Yancey } else if (binary || STAILQ_EMPTY(&editlist)) { 8607e32b20dSKelly Yancey /* Display without formatting information. */ 8617e32b20dSKelly Yancey modepage_dump(device, page, page_control, dbd, retry_count, 8627e32b20dSKelly Yancey timeout); 863525689f1SJustin T. Gibbs } else { 8647e32b20dSKelly Yancey /* Display with format. */ 8657e32b20dSKelly Yancey modepage_write(stdout, 0); 8667e32b20dSKelly Yancey } 8677e32b20dSKelly Yancey } 8687e32b20dSKelly Yancey 8697e32b20dSKelly Yancey void 8707e32b20dSKelly Yancey mode_list(struct cam_device *device, int page_control, int dbd, 8717e32b20dSKelly Yancey int retry_count, int timeout) 8727e32b20dSKelly Yancey { 8737e32b20dSKelly Yancey u_int8_t data[MAX_COMMAND_SIZE];/* Buffer to hold sense data. */ 8747e32b20dSKelly Yancey u_int8_t *mode_pars; /* Pointer to modepage params. */ 8757e32b20dSKelly Yancey struct scsi_mode_header_6 *mh; /* Location of mode header. */ 8767e32b20dSKelly Yancey struct scsi_mode_page_header *mph; 8777e32b20dSKelly Yancey struct pagename *nameentry; 8787e32b20dSKelly Yancey char *pagedb_path; 8797e32b20dSKelly Yancey int len; 8807e32b20dSKelly Yancey 8817e32b20dSKelly Yancey if ((pagedb_path = getenv("SCSI_MODES")) == NULL) 8827e32b20dSKelly Yancey pagedb_path = DEFAULT_SCSI_MODE_DB; 8837e32b20dSKelly Yancey 8847e32b20dSKelly Yancey if (load_format(pagedb_path, 0) != 0 && verbose && errno == ENOENT) { 8857e32b20dSKelly Yancey /* Modepage database file not found. */ 8867e32b20dSKelly Yancey warn("cannot open modepage database \"%s\"", pagedb_path); 8877e32b20dSKelly Yancey } 8887e32b20dSKelly Yancey 8897e32b20dSKelly Yancey /* Build the list of all mode pages by querying the "all pages" page. */ 8907e32b20dSKelly Yancey mode_sense(device, SMS_ALL_PAGES_PAGE, page_control, dbd, retry_count, 8917e32b20dSKelly Yancey timeout, data, sizeof(data)); 8927e32b20dSKelly Yancey 8937e32b20dSKelly Yancey mh = (struct scsi_mode_header_6 *)data; 8947e32b20dSKelly Yancey len = mh->blk_desc_len; /* Skip block descriptors. */ 8957e32b20dSKelly Yancey /* Iterate through the pages in the reply. */ 8967e32b20dSKelly Yancey while (len < mh->data_length) { 8977e32b20dSKelly Yancey /* Locate the next mode page header. */ 8987e32b20dSKelly Yancey mph = (struct scsi_mode_page_header *) 8997e32b20dSKelly Yancey ((intptr_t)mh + sizeof(*mh) + len); 9007e32b20dSKelly Yancey mode_pars = MODE_PAGE_DATA(mph); 9017e32b20dSKelly Yancey 9027e32b20dSKelly Yancey mph->page_code &= SMS_PAGE_CODE; 9037e32b20dSKelly Yancey nameentry = nameentry_lookup(mph->page_code); 9047e32b20dSKelly Yancey 9057e32b20dSKelly Yancey if (nameentry == NULL || nameentry->name == NULL) 9067e32b20dSKelly Yancey printf("0x%02x\n", mph->page_code); 9077e32b20dSKelly Yancey else 9087e32b20dSKelly Yancey printf("0x%02x\t%s\n", mph->page_code, 9097e32b20dSKelly Yancey nameentry->name); 9107e32b20dSKelly Yancey len += mph->page_length + sizeof(*mph); 911525689f1SJustin T. Gibbs } 912525689f1SJustin T. Gibbs } 913