xref: /freebsd/sbin/camcontrol/modeedit.c (revision 3d438ad61f57a1129d2db01947a14bf0fc86dbae)
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