xref: /freebsd/sbin/camcontrol/modeedit.c (revision 1d386b48a555f61cb7325543adbbb5c3f3407a66)
17e32b20dSKelly Yancey /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro F. Giffuni  *
47e32b20dSKelly Yancey  * Copyright (c) 2000 Kelly Yancey <kbyanc@posi.net>
57e32b20dSKelly Yancey  * Derived from work done by Julian Elischer <julian@tfs.com,
67e32b20dSKelly Yancey  * julian@dialix.oz.au>, 1993, and Peter Dufault <dufault@hda.com>, 1994.
7525689f1SJustin T. Gibbs  * All rights reserved.
8525689f1SJustin T. Gibbs  *
9525689f1SJustin T. Gibbs  * Redistribution and use in source and binary forms, with or without
10525689f1SJustin T. Gibbs  * modification, are permitted provided that the following conditions
11525689f1SJustin T. Gibbs  * are met:
12525689f1SJustin T. Gibbs  * 1. Redistributions of source code must retain the above copyright
137e32b20dSKelly Yancey  *    notice, this list of conditions and the following disclaimer,
147e32b20dSKelly Yancey  *    without modification, immediately at the beginning of the file.
15525689f1SJustin T. Gibbs  * 2. Redistributions in binary form must reproduce the above copyright
16525689f1SJustin T. Gibbs  *    notice, this list of conditions and the following disclaimer in the
17525689f1SJustin T. Gibbs  *    documentation and/or other materials provided with the distribution.
18525689f1SJustin T. Gibbs  *
197e32b20dSKelly Yancey  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
207e32b20dSKelly Yancey  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
217e32b20dSKelly Yancey  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
227e32b20dSKelly Yancey  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
237e32b20dSKelly Yancey  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
247e32b20dSKelly Yancey  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
257e32b20dSKelly Yancey  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
267e32b20dSKelly Yancey  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
277e32b20dSKelly Yancey  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
287e32b20dSKelly Yancey  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29525689f1SJustin T. Gibbs  */
307e32b20dSKelly Yancey 
31bf2783a0SDavid E. O'Brien #include <sys/cdefs.h>
327e32b20dSKelly Yancey #include <sys/queue.h>
337e32b20dSKelly Yancey #include <sys/types.h>
3409128776SAlexander Motin #include <sys/sbuf.h>
357e32b20dSKelly Yancey 
367e32b20dSKelly Yancey #include <assert.h>
37525689f1SJustin T. Gibbs #include <ctype.h>
38525689f1SJustin T. Gibbs #include <err.h>
39525689f1SJustin T. Gibbs #include <errno.h>
40525689f1SJustin T. Gibbs #include <stdlib.h>
417e32b20dSKelly Yancey #include <string.h>
42525689f1SJustin T. Gibbs #include <stdio.h>
437e32b20dSKelly Yancey #include <sysexits.h>
44525689f1SJustin T. Gibbs #include <unistd.h>
45525689f1SJustin T. Gibbs 
467e32b20dSKelly Yancey #include <cam/scsi/scsi_all.h>
47525689f1SJustin T. Gibbs #include <cam/cam.h>
48525689f1SJustin T. Gibbs #include <cam/cam_ccb.h>
49525689f1SJustin T. Gibbs #include <camlib.h>
50525689f1SJustin T. Gibbs #include "camcontrol.h"
51525689f1SJustin T. Gibbs 
527e32b20dSKelly Yancey #define	DEFAULT_SCSI_MODE_DB	"/usr/share/misc/scsi_modes"
537e32b20dSKelly Yancey #define	DEFAULT_EDITOR		"vi"
547e32b20dSKelly Yancey #define	MAX_FORMAT_SPEC		4096	/* Max CDB format specifier. */
557e32b20dSKelly Yancey #define	MAX_PAGENUM_LEN		10	/* Max characters in page num. */
567e32b20dSKelly Yancey #define	MAX_PAGENAME_LEN	64	/* Max characters in page name. */
577e32b20dSKelly Yancey #define	PAGEDEF_START		'{'	/* Page definition delimiter. */
587e32b20dSKelly Yancey #define	PAGEDEF_END		'}'	/* Page definition delimiter. */
597e32b20dSKelly Yancey #define	PAGENAME_START		'"'	/* Page name delimiter. */
607e32b20dSKelly Yancey #define	PAGENAME_END		'"'	/* Page name delimiter. */
617e32b20dSKelly Yancey #define	PAGEENTRY_END		';'	/* Page entry terminator (optional). */
62e341cfd2SAlexander Motin #define	MAX_DATA_SIZE		4096	/* Mode/Log sense data buffer size. */
63354a0adaSKelly Yancey #define PAGE_CTRL_SHIFT		6	/* Bit offset to page control field. */
647e32b20dSKelly Yancey 
657e32b20dSKelly Yancey struct editentry {
667e32b20dSKelly Yancey 	STAILQ_ENTRY(editentry) link;
677e32b20dSKelly Yancey 	char	*name;
687e32b20dSKelly Yancey 	char	type;
697e32b20dSKelly Yancey 	int	editable;
707e32b20dSKelly Yancey 	int	size;
717e32b20dSKelly Yancey 	union {
727e32b20dSKelly Yancey 		int	ivalue;
737e32b20dSKelly Yancey 		char	*svalue;
747e32b20dSKelly Yancey 	} value;
757e32b20dSKelly Yancey };
765111cde1SEd Schouten static STAILQ_HEAD(, editentry) editlist; /* List of page entries. */
775111cde1SEd Schouten static int editlist_changed = 0;	/* Whether any entries were changed. */
787e32b20dSKelly Yancey 
797e32b20dSKelly Yancey struct pagename {
807e32b20dSKelly Yancey 	SLIST_ENTRY(pagename) link;
8154644e21SAlexander Motin 	int page;
8254644e21SAlexander Motin 	int subpage;
837e32b20dSKelly Yancey 	char *name;
847e32b20dSKelly Yancey };
855111cde1SEd Schouten static SLIST_HEAD(, pagename) namelist;	/* Page number to name mappings. */
867e32b20dSKelly Yancey 
877e32b20dSKelly Yancey static char format[MAX_FORMAT_SPEC];	/* Buffer for scsi cdb format def. */
887e32b20dSKelly Yancey 
897e32b20dSKelly Yancey static FILE *edit_file = NULL;		/* File handle for edit file. */
907e32b20dSKelly Yancey static char edit_path[] = "/tmp/camXXXXXX";
917e32b20dSKelly Yancey 
927e32b20dSKelly Yancey 
937e32b20dSKelly Yancey /* Function prototypes. */
947e32b20dSKelly Yancey static void		 editentry_create(void *hook, int letter, void *arg,
957e32b20dSKelly Yancey 					  int count, char *name);
967e32b20dSKelly Yancey static void		 editentry_update(void *hook, int letter, void *arg,
977e32b20dSKelly Yancey 					  int count, char *name);
9809128776SAlexander Motin static void		 editentry_create_desc(void *hook, int letter, void *arg,
9909128776SAlexander Motin 					  int count, char *name);
1007e32b20dSKelly Yancey static int		 editentry_save(void *hook, char *name);
1017e32b20dSKelly Yancey static struct editentry	*editentry_lookup(char *name);
1027e32b20dSKelly Yancey static int		 editentry_set(char *name, char *newvalue,
1037e32b20dSKelly Yancey 				       int editonly);
104e341cfd2SAlexander Motin static void		 editlist_populate(struct cam_device *device,
105e341cfd2SAlexander Motin 			    int cdb_len, int dbd, int pc, int page, int subpage,
106e341cfd2SAlexander Motin 			    int task_attr, int retries, int timeout);
10709128776SAlexander Motin static void		 editlist_populate_desc(struct cam_device *device,
10809128776SAlexander Motin 			    int cdb_len, int llbaa, int pc, int page,
10909128776SAlexander Motin 			    int subpage, int task_attr, int retries,
11009128776SAlexander Motin 			    int timeout);
111e341cfd2SAlexander Motin static void		 editlist_save(struct cam_device *device, int cdb_len,
112e341cfd2SAlexander Motin 			    int dbd, int pc, int page, int subpage,
113492a2ef5SKenneth D. Merry 			    int task_attr, int retries, int timeout);
11409128776SAlexander Motin static void		 editlist_save_desc(struct cam_device *device, int cdb_len,
11509128776SAlexander Motin 			    int llbaa, int pc, int page, int subpage,
11609128776SAlexander Motin 			    int task_attr, int retries, int timeout);
11754644e21SAlexander Motin static void		 nameentry_create(int page, int subpage, char *name);
11854644e21SAlexander Motin static struct pagename	*nameentry_lookup(int page, int subpage);
11954644e21SAlexander Motin static int		 load_format(const char *pagedb_path, int lpage,
12054644e21SAlexander Motin 			    int lsubpage);
1217e32b20dSKelly Yancey static int		 modepage_write(FILE *file, int editonly);
1227e32b20dSKelly Yancey static int		 modepage_read(FILE *file);
1237e32b20dSKelly Yancey static void		 modepage_edit(void);
124e341cfd2SAlexander Motin static void		 modepage_dump(struct cam_device *device, int cdb_len,
125e341cfd2SAlexander Motin 			    int dbd, int pc, int page, int subpage,
126e341cfd2SAlexander Motin 			    int task_attr, int retries, int timeout);
12709128776SAlexander Motin static void		 modepage_dump_desc(struct cam_device *device,
12809128776SAlexander Motin 			    int cdb_len, int llbaa, int pc, int page,
12909128776SAlexander Motin 			    int subpage, int task_attr, int retries,
13009128776SAlexander Motin 			    int timeout);
1317e32b20dSKelly Yancey static void		 cleanup_editfile(void);
1327e32b20dSKelly Yancey 
1337e32b20dSKelly Yancey 
1347e32b20dSKelly Yancey #define	returnerr(code) do {						\
1357e32b20dSKelly Yancey 	errno = code;							\
1367e32b20dSKelly Yancey 	return (-1);							\
1377e32b20dSKelly Yancey } while (0)
1387e32b20dSKelly Yancey 
1397e32b20dSKelly Yancey 
1407e32b20dSKelly Yancey #define	RTRIM(string) do {						\
1413d438ad6SDavid E. O'Brien 	int _length;							\
1427e32b20dSKelly Yancey 	while (isspace(string[_length = strlen(string) - 1]))		\
1437e32b20dSKelly Yancey 		string[_length] = '\0';					\
1447e32b20dSKelly Yancey } while (0)
1457e32b20dSKelly Yancey 
1467e32b20dSKelly Yancey 
1477e32b20dSKelly Yancey static void
editentry_create(void * hook __unused,int letter,void * arg,int count,char * name)14839867613SJohan Karlsson editentry_create(void *hook __unused, int letter, void *arg, int count,
14939867613SJohan Karlsson 		 char *name)
150525689f1SJustin T. Gibbs {
1517e32b20dSKelly Yancey 	struct editentry *newentry;	/* Buffer to hold new entry. */
152525689f1SJustin T. Gibbs 
1537e32b20dSKelly Yancey 	/* Allocate memory for the new entry and a copy of the entry name. */
1547e32b20dSKelly Yancey 	if ((newentry = malloc(sizeof(struct editentry))) == NULL ||
1557e32b20dSKelly Yancey 	    (newentry->name = strdup(name)) == NULL)
1567e32b20dSKelly Yancey 		err(EX_OSERR, NULL);
157525689f1SJustin T. Gibbs 
1587e32b20dSKelly Yancey 	/* Trim any trailing whitespace for the entry name. */
1597e32b20dSKelly Yancey 	RTRIM(newentry->name);
160525689f1SJustin T. Gibbs 
1617e32b20dSKelly Yancey 	newentry->editable = (arg != NULL);
1627e32b20dSKelly Yancey 	newentry->type = letter;
1637e32b20dSKelly Yancey 	newentry->size = count;		/* Placeholder; not accurate. */
1647e32b20dSKelly Yancey 	newentry->value.svalue = NULL;
1657e32b20dSKelly Yancey 
1667e32b20dSKelly Yancey 	STAILQ_INSERT_TAIL(&editlist, newentry, link);
167525689f1SJustin T. Gibbs }
168525689f1SJustin T. Gibbs 
1697e32b20dSKelly Yancey static void
editentry_update(void * hook __unused,int letter,void * arg,int count,char * name)17039867613SJohan Karlsson editentry_update(void *hook __unused, int letter, void *arg, int count,
17139867613SJohan Karlsson 		 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:
193b055b157SGarrett Wollman 		; /* NOTREACHED */
194525689f1SJustin T. Gibbs 	}
195525689f1SJustin T. Gibbs }
196525689f1SJustin T. Gibbs 
19709128776SAlexander Motin static void
editentry_create_desc(void * hook __unused,int letter,void * arg,int count,char * name)19809128776SAlexander Motin editentry_create_desc(void *hook __unused, int letter, void *arg, int count,
19909128776SAlexander Motin 		 char *name)
20009128776SAlexander Motin {
20109128776SAlexander Motin 	struct editentry *newentry;	/* Buffer to hold new entry. */
20209128776SAlexander Motin 
20309128776SAlexander Motin 	/* Allocate memory for the new entry and a copy of the entry name. */
20409128776SAlexander Motin 	if ((newentry = malloc(sizeof(struct editentry))) == NULL ||
20509128776SAlexander Motin 	    (newentry->name = strdup(name)) == NULL)
20609128776SAlexander Motin 		err(EX_OSERR, NULL);
20709128776SAlexander Motin 
20809128776SAlexander Motin 	/* Trim any trailing whitespace for the entry name. */
20909128776SAlexander Motin 	RTRIM(newentry->name);
21009128776SAlexander Motin 
21109128776SAlexander Motin 	newentry->editable = 1;
21209128776SAlexander Motin 	newentry->type = letter;
21309128776SAlexander Motin 	newentry->size = count;
21409128776SAlexander Motin 	newentry->value.svalue = NULL;
21509128776SAlexander Motin 
21609128776SAlexander Motin 	STAILQ_INSERT_TAIL(&editlist, newentry, link);
21709128776SAlexander Motin 
21809128776SAlexander Motin 	switch (letter) {
21909128776SAlexander Motin 	case 'i':			/* Byte-sized integral type. */
22009128776SAlexander Motin 	case 'b':			/* Bit-sized integral types. */
22109128776SAlexander Motin 	case 't':
22209128776SAlexander Motin 		newentry->value.ivalue = (intptr_t)arg;
22309128776SAlexander Motin 		break;
22409128776SAlexander Motin 
22509128776SAlexander Motin 	case 'c':			/* Character array. */
22609128776SAlexander Motin 	case 'z':			/* Null-padded string. */
22709128776SAlexander Motin 		editentry_set(name, (char *)arg, 0);
22809128776SAlexander Motin 		break;
22909128776SAlexander Motin 	default:
23009128776SAlexander Motin 		; /* NOTREACHED */
23109128776SAlexander Motin 	}
23209128776SAlexander Motin }
23309128776SAlexander Motin 
234525689f1SJustin T. Gibbs static int
editentry_save(void * hook __unused,char * name)23539867613SJohan Karlsson editentry_save(void *hook __unused, char *name)
236525689f1SJustin T. Gibbs {
2377e32b20dSKelly Yancey 	struct editentry *src;		/* Entry value to save. */
238525689f1SJustin T. Gibbs 
2397e32b20dSKelly Yancey 	src = editentry_lookup(name);
24026b1288fSAlexander Motin 	if (src == 0) {
24126b1288fSAlexander Motin 		/*
24226b1288fSAlexander Motin 		 * This happens if field does not fit into read page size.
24326b1288fSAlexander Motin 		 * It also means that this field won't be written, so the
24426b1288fSAlexander Motin 		 * returned value does not really matter.
24526b1288fSAlexander Motin 		 */
24626b1288fSAlexander Motin 		return (0);
24726b1288fSAlexander Motin 	}
248525689f1SJustin T. Gibbs 
2497e32b20dSKelly Yancey 	switch (src->type) {
2507e32b20dSKelly Yancey 	case 'i':			/* Byte-sized integral type. */
2517e32b20dSKelly Yancey 	case 'b':			/* Bit-sized integral types. */
2527e32b20dSKelly Yancey 	case 't':
2537e32b20dSKelly Yancey 		return (src->value.ivalue);
2547e32b20dSKelly Yancey 		/* NOTREACHED */
255525689f1SJustin T. Gibbs 
2567e32b20dSKelly Yancey 	case 'c':			/* Character array. */
2577e32b20dSKelly Yancey 	case 'z':			/* Null-padded string. */
2587e32b20dSKelly Yancey 		return ((intptr_t)src->value.svalue);
2597e32b20dSKelly Yancey 		/* NOTREACHED */
260525689f1SJustin T. Gibbs 
2617e32b20dSKelly Yancey 	default:
262b055b157SGarrett Wollman 		; /* NOTREACHED */
263525689f1SJustin T. Gibbs 	}
264525689f1SJustin T. Gibbs 
2657e32b20dSKelly Yancey 	return (0);			/* This should never happen. */
2667e32b20dSKelly Yancey }
2677e32b20dSKelly Yancey 
2687e32b20dSKelly Yancey static struct editentry *
editentry_lookup(char * name)2697e32b20dSKelly Yancey editentry_lookup(char *name)
2707e32b20dSKelly Yancey {
2717e32b20dSKelly Yancey 	struct editentry *scan;
2727e32b20dSKelly Yancey 
2737e32b20dSKelly Yancey 	assert(name != NULL);
2747e32b20dSKelly Yancey 
2757e32b20dSKelly Yancey 	STAILQ_FOREACH(scan, &editlist, link) {
2767e32b20dSKelly Yancey 		if (strcasecmp(scan->name, name) == 0)
2777e32b20dSKelly Yancey 			return (scan);
2787e32b20dSKelly Yancey 	}
2797e32b20dSKelly Yancey 
2807e32b20dSKelly Yancey 	/* Not found during list traversal. */
2817e32b20dSKelly Yancey 	return (NULL);
2827e32b20dSKelly Yancey }
2837e32b20dSKelly Yancey 
2847e32b20dSKelly Yancey static int
editentry_set(char * name,char * newvalue,int editonly)2857e32b20dSKelly Yancey editentry_set(char *name, char *newvalue, int editonly)
2867e32b20dSKelly Yancey {
2877e32b20dSKelly Yancey 	struct editentry *dest;	/* Modepage entry to update. */
2887e32b20dSKelly Yancey 	char *cval;		/* Pointer to new string value. */
2897e32b20dSKelly Yancey 	char *convertend;	/* End-of-conversion pointer. */
29009128776SAlexander Motin 	long long ival, newival; /* New integral value. */
2917e32b20dSKelly Yancey 	int resolution;		/* Resolution in bits for integer conversion. */
2927e32b20dSKelly Yancey 
2937e32b20dSKelly Yancey /*
2947e32b20dSKelly Yancey  * Macro to determine the maximum value of the given size for the current
2957e32b20dSKelly Yancey  * resolution.
2967e32b20dSKelly Yancey  */
297b7e08f93SAlexander Motin #define	RESOLUTION_MAX(size)	((1LL << (resolution * (size))) - 1)
2987e32b20dSKelly Yancey 
2997e32b20dSKelly Yancey 	assert(newvalue != NULL);
3007e32b20dSKelly Yancey 	if (*newvalue == '\0')
3017e32b20dSKelly Yancey 		return (0);	/* Nothing to do. */
3027e32b20dSKelly Yancey 
3037e32b20dSKelly Yancey 	if ((dest = editentry_lookup(name)) == NULL)
3047e32b20dSKelly Yancey 		returnerr(ENOENT);
3057e32b20dSKelly Yancey 	if (!dest->editable && editonly)
3067e32b20dSKelly Yancey 		returnerr(EPERM);
3077e32b20dSKelly Yancey 
3087e32b20dSKelly Yancey 	switch (dest->type) {
3097e32b20dSKelly Yancey 	case 'i':		/* Byte-sized integral type. */
3107e32b20dSKelly Yancey 	case 'b':		/* Bit-sized integral types. */
3117e32b20dSKelly Yancey 	case 't':
3127e32b20dSKelly Yancey 		/* Convert the value string to an integer. */
3137e32b20dSKelly Yancey 		resolution = (dest->type == 'i')? 8: 1;
31409128776SAlexander Motin 		ival = strtoll(newvalue, &convertend, 0);
3157e32b20dSKelly Yancey 		if (*convertend != '\0')
3167e32b20dSKelly Yancey 			returnerr(EINVAL);
3177e32b20dSKelly Yancey 		if (ival > RESOLUTION_MAX(dest->size) || ival < 0) {
31809128776SAlexander Motin 			newival = (ival < 0) ? 0 : RESOLUTION_MAX(dest->size);
31909128776SAlexander Motin 			warnx("value %lld is out of range for entry %s; "
32009128776SAlexander Motin 			    "clipping to %lld", ival, name, newival);
3217e32b20dSKelly Yancey 			ival = newival;
3227e32b20dSKelly Yancey 		}
3237e32b20dSKelly Yancey 		if (dest->value.ivalue != ival)
3247e32b20dSKelly Yancey 			editlist_changed = 1;
3257e32b20dSKelly Yancey 		dest->value.ivalue = ival;
3267e32b20dSKelly Yancey 		break;
3277e32b20dSKelly Yancey 
3287e32b20dSKelly Yancey 	case 'c':		/* Character array. */
3297e32b20dSKelly Yancey 	case 'z':		/* Null-padded string. */
330fd340a12SXin LI 		if ((cval = calloc(1, dest->size + 1)) == NULL)
3317e32b20dSKelly Yancey 			err(EX_OSERR, NULL);
332fd340a12SXin LI 		strlcpy(cval, newvalue, dest->size + 1);
3337e32b20dSKelly Yancey 		if (dest->type == 'z') {
3347e32b20dSKelly Yancey 			/* Convert trailing spaces to nulls. */
33539867613SJohan Karlsson 			char *convertend2;
3367e32b20dSKelly Yancey 
33739867613SJohan Karlsson 			for (convertend2 = cval + dest->size;
33839867613SJohan Karlsson 			    convertend2 >= cval; convertend2--) {
33939867613SJohan Karlsson 				if (*convertend2 == ' ')
34039867613SJohan Karlsson 					*convertend2 = '\0';
34139867613SJohan Karlsson 				else if (*convertend2 != '\0')
3427e32b20dSKelly Yancey 					break;
3437e32b20dSKelly Yancey 			}
3447e32b20dSKelly Yancey 		}
3457e32b20dSKelly Yancey 		if (strncmp(dest->value.svalue, cval, dest->size) == 0) {
3467e32b20dSKelly Yancey 			/* Nothing changed, free the newly allocated string. */
3477e32b20dSKelly Yancey 			free(cval);
3487e32b20dSKelly Yancey 			break;
3497e32b20dSKelly Yancey 		}
3507e32b20dSKelly Yancey 		if (dest->value.svalue != NULL) {
3517e32b20dSKelly Yancey 			/* Free the current string buffer. */
3527e32b20dSKelly Yancey 			free(dest->value.svalue);
3537e32b20dSKelly Yancey 			dest->value.svalue = NULL;
3547e32b20dSKelly Yancey 		}
3557e32b20dSKelly Yancey 		dest->value.svalue = cval;
3567e32b20dSKelly Yancey 		editlist_changed = 1;
3577e32b20dSKelly Yancey 		break;
3587e32b20dSKelly Yancey 
3597e32b20dSKelly Yancey 	default:
360b055b157SGarrett Wollman 		; /* NOTREACHED */
3617e32b20dSKelly Yancey 	}
3627e32b20dSKelly Yancey 
3637e32b20dSKelly Yancey 	return (0);
3647e32b20dSKelly Yancey #undef RESOLUTION_MAX
365525689f1SJustin T. Gibbs }
366525689f1SJustin T. Gibbs 
367525689f1SJustin T. Gibbs static void
nameentry_create(int page,int subpage,char * name)36854644e21SAlexander Motin nameentry_create(int page, int subpage, char *name) {
3697e32b20dSKelly Yancey 	struct pagename *newentry;
3707e32b20dSKelly Yancey 
37154644e21SAlexander Motin 	if (page < 0 || subpage < 0 || name == NULL || name[0] == '\0')
3727e32b20dSKelly Yancey 		return;
3737e32b20dSKelly Yancey 
3747e32b20dSKelly Yancey 	/* Allocate memory for the new entry and a copy of the entry name. */
3757e32b20dSKelly Yancey 	if ((newentry = malloc(sizeof(struct pagename))) == NULL ||
3767e32b20dSKelly Yancey 	    (newentry->name = strdup(name)) == NULL)
3777e32b20dSKelly Yancey 		err(EX_OSERR, NULL);
3787e32b20dSKelly Yancey 
3797e32b20dSKelly Yancey 	/* Trim any trailing whitespace for the page name. */
3807e32b20dSKelly Yancey 	RTRIM(newentry->name);
3817e32b20dSKelly Yancey 
38254644e21SAlexander Motin 	newentry->page = page;
38354644e21SAlexander Motin 	newentry->subpage = subpage;
3847e32b20dSKelly Yancey 	SLIST_INSERT_HEAD(&namelist, newentry, link);
3857e32b20dSKelly Yancey }
3867e32b20dSKelly Yancey 
3877e32b20dSKelly Yancey static struct pagename *
nameentry_lookup(int page,int subpage)38854644e21SAlexander Motin nameentry_lookup(int page, int subpage) {
3897e32b20dSKelly Yancey 	struct pagename *scan;
3907e32b20dSKelly Yancey 
3917e32b20dSKelly Yancey 	SLIST_FOREACH(scan, &namelist, link) {
39254644e21SAlexander Motin 		if (page == scan->page && subpage == scan->subpage)
3937e32b20dSKelly Yancey 			return (scan);
3947e32b20dSKelly Yancey 	}
3957e32b20dSKelly Yancey 
3967e32b20dSKelly Yancey 	/* Not found during list traversal. */
3977e32b20dSKelly Yancey 	return (NULL);
3987e32b20dSKelly Yancey }
3997e32b20dSKelly Yancey 
4007e32b20dSKelly Yancey static int
load_format(const char * pagedb_path,int lpage,int lsubpage)40154644e21SAlexander Motin load_format(const char *pagedb_path, int lpage, int lsubpage)
402525689f1SJustin T. Gibbs {
4037e32b20dSKelly Yancey 	FILE *pagedb;
40454644e21SAlexander Motin 	char str_page[MAX_PAGENUM_LEN];
40554644e21SAlexander Motin 	char *str_subpage;
4067e32b20dSKelly Yancey 	char str_pagename[MAX_PAGENAME_LEN];
40754644e21SAlexander Motin 	int page;
40854644e21SAlexander Motin 	int subpage;
4097e32b20dSKelly Yancey 	int depth;			/* Quoting depth. */
4107e32b20dSKelly Yancey 	int found;
4117e32b20dSKelly Yancey 	int lineno;
4127e32b20dSKelly Yancey 	enum { LOCATE, PAGENAME, PAGEDEF } state;
4135cfe0423SPeter Grehan 	int ch;
4147e32b20dSKelly Yancey 	char c;
4157e32b20dSKelly Yancey 
4167e32b20dSKelly Yancey #define	SETSTATE_LOCATE do {						\
41754644e21SAlexander Motin 	str_page[0] = '\0';						\
4187e32b20dSKelly Yancey 	str_pagename[0] = '\0';						\
41954644e21SAlexander Motin 	page = -1;							\
42054644e21SAlexander Motin 	subpage = -1;							\
4217e32b20dSKelly Yancey 	state = LOCATE;							\
4227e32b20dSKelly Yancey } while (0)
4237e32b20dSKelly Yancey 
4247e32b20dSKelly Yancey #define	SETSTATE_PAGENAME do {						\
4257e32b20dSKelly Yancey 	str_pagename[0] = '\0';						\
4267e32b20dSKelly Yancey 	state = PAGENAME;						\
4277e32b20dSKelly Yancey } while (0)
4287e32b20dSKelly Yancey 
4297e32b20dSKelly Yancey #define	SETSTATE_PAGEDEF do {						\
4307e32b20dSKelly Yancey 	format[0] = '\0';						\
4317e32b20dSKelly Yancey 	state = PAGEDEF;						\
4327e32b20dSKelly Yancey } while (0)
4337e32b20dSKelly Yancey 
4347e32b20dSKelly Yancey #define	UPDATE_LINENO do {						\
4357e32b20dSKelly Yancey 	if (c == '\n')							\
4367e32b20dSKelly Yancey 		lineno++;						\
4377e32b20dSKelly Yancey } while (0)
4387e32b20dSKelly Yancey 
4397e32b20dSKelly Yancey #define	BUFFERFULL(buffer)	(strlen(buffer) + 1 >= sizeof(buffer))
4407e32b20dSKelly Yancey 
4417e32b20dSKelly Yancey 	if ((pagedb = fopen(pagedb_path, "r")) == NULL)
4427e32b20dSKelly Yancey 		returnerr(ENOENT);
4437e32b20dSKelly Yancey 
4447e32b20dSKelly Yancey 	SLIST_INIT(&namelist);
4457e32b20dSKelly Yancey 
446ee4912ebSUlrich Spörlein 	c = '\0';
4477e32b20dSKelly Yancey 	depth = 0;
4487e32b20dSKelly Yancey 	lineno = 0;
4497e32b20dSKelly Yancey 	found = 0;
4507e32b20dSKelly Yancey 	SETSTATE_LOCATE;
4515cfe0423SPeter Grehan 	while ((ch = fgetc(pagedb)) != EOF) {
4527e32b20dSKelly Yancey 
4537e32b20dSKelly Yancey 		/* Keep a line count to make error messages more useful. */
4547e32b20dSKelly Yancey 		UPDATE_LINENO;
4557e32b20dSKelly Yancey 
4567e32b20dSKelly Yancey 		/* Skip over comments anywhere in the mode database. */
4575cfe0423SPeter Grehan 		if (ch == '#') {
4587e32b20dSKelly Yancey 			do {
4595cfe0423SPeter Grehan 				ch = fgetc(pagedb);
4605cfe0423SPeter Grehan 			} while (ch != '\n' && ch != EOF);
4617e32b20dSKelly Yancey 			UPDATE_LINENO;
4627e32b20dSKelly Yancey 			continue;
4637e32b20dSKelly Yancey 		}
4645cfe0423SPeter Grehan 		c = ch;
4657e32b20dSKelly Yancey 
4667e32b20dSKelly Yancey 		/* Strip out newline characters. */
4677e32b20dSKelly Yancey 		if (c == '\n')
4687e32b20dSKelly Yancey 			continue;
4697e32b20dSKelly Yancey 
4707e32b20dSKelly Yancey 		/* Keep track of the nesting depth for braces. */
4717e32b20dSKelly Yancey 		if (c == PAGEDEF_START)
4727e32b20dSKelly Yancey 			depth++;
4737e32b20dSKelly Yancey 		else if (c == PAGEDEF_END) {
4747e32b20dSKelly Yancey 			depth--;
4757e32b20dSKelly Yancey 			if (depth < 0) {
4767e32b20dSKelly Yancey 				errx(EX_OSFILE, "%s:%d: %s", pagedb_path,
4777e32b20dSKelly Yancey 				    lineno, "mismatched bracket");
4787e32b20dSKelly Yancey 			}
4797e32b20dSKelly Yancey 		}
4807e32b20dSKelly Yancey 
4817e32b20dSKelly Yancey 		switch (state) {
4827e32b20dSKelly Yancey 		case LOCATE:
4837e32b20dSKelly Yancey 			/*
4847e32b20dSKelly Yancey 			 * Locate the page the user is interested in, skipping
4857e32b20dSKelly Yancey 			 * all others.
4867e32b20dSKelly Yancey 			 */
4877e32b20dSKelly Yancey 			if (isspace(c)) {
4887e32b20dSKelly Yancey 				/* Ignore all whitespace between pages. */
4897e32b20dSKelly Yancey 				break;
4907e32b20dSKelly Yancey 			} else if (depth == 0 && c == PAGEENTRY_END) {
4917e32b20dSKelly Yancey 				/*
4927e32b20dSKelly Yancey 				 * A page entry terminator will reset page
4937e32b20dSKelly Yancey 				 * scanning (useful for assigning names to
4947e32b20dSKelly Yancey 				 * modes without providing a mode definition).
4957e32b20dSKelly Yancey 				 */
4967e32b20dSKelly Yancey 				/* Record the name of this page. */
49754644e21SAlexander Motin 				str_subpage = str_page;
49854644e21SAlexander Motin 				strsep(&str_subpage, ",");
49954644e21SAlexander Motin 				page = strtol(str_page, NULL, 0);
50054644e21SAlexander Motin 				if (str_subpage)
50154644e21SAlexander Motin 				    subpage = strtol(str_subpage, NULL, 0);
50254644e21SAlexander Motin 				else
50354644e21SAlexander Motin 				    subpage = 0;
50454644e21SAlexander Motin 				nameentry_create(page, subpage, str_pagename);
5057e32b20dSKelly Yancey 				SETSTATE_LOCATE;
5067e32b20dSKelly Yancey 			} else if (depth == 0 && c == PAGENAME_START) {
5077e32b20dSKelly Yancey 				SETSTATE_PAGENAME;
5087e32b20dSKelly Yancey 			} else if (c == PAGEDEF_START) {
50954644e21SAlexander Motin 				str_subpage = str_page;
51054644e21SAlexander Motin 				strsep(&str_subpage, ",");
51154644e21SAlexander Motin 				page = strtol(str_page, NULL, 0);
51254644e21SAlexander Motin 				if (str_subpage)
51354644e21SAlexander Motin 				    subpage = strtol(str_subpage, NULL, 0);
51454644e21SAlexander Motin 				else
51554644e21SAlexander Motin 				    subpage = 0;
5167e32b20dSKelly Yancey 				if (depth == 1) {
5177e32b20dSKelly Yancey 					/* Record the name of this page. */
51854644e21SAlexander Motin 					nameentry_create(page, subpage,
51954644e21SAlexander Motin 					    str_pagename);
5207e32b20dSKelly Yancey 					/*
5217e32b20dSKelly Yancey 					 * Only record the format if this is
5227e32b20dSKelly Yancey 					 * the page we are interested in.
5237e32b20dSKelly Yancey 					 */
52454644e21SAlexander Motin 					if (lpage == page &&
52554644e21SAlexander Motin 					    lsubpage == subpage && !found)
5267e32b20dSKelly Yancey 						SETSTATE_PAGEDEF;
5277e32b20dSKelly Yancey 				}
5287e32b20dSKelly Yancey 			} else if (c == PAGEDEF_END) {
5297e32b20dSKelly Yancey 				/* Reset the processor state. */
5307e32b20dSKelly Yancey 				SETSTATE_LOCATE;
53154644e21SAlexander Motin 			} else if (depth == 0 && ! BUFFERFULL(str_page)) {
53254644e21SAlexander Motin 				strncat(str_page, &c, 1);
5337e32b20dSKelly Yancey 			} else if (depth == 0) {
534024ae004SRuslan Ermilov 				errx(EX_OSFILE, "%s:%d: %s %zd %s", pagedb_path,
5357e32b20dSKelly Yancey 				    lineno, "page identifier exceeds",
53654644e21SAlexander Motin 				    sizeof(str_page) - 1, "characters");
5377e32b20dSKelly Yancey 			}
5387e32b20dSKelly Yancey 			break;
5397e32b20dSKelly Yancey 
5407e32b20dSKelly Yancey 		case PAGENAME:
5417e32b20dSKelly Yancey 			if (c == PAGENAME_END) {
5427e32b20dSKelly Yancey 				/*
5437e32b20dSKelly Yancey 				 * Return to LOCATE state without resetting the
5447e32b20dSKelly Yancey 				 * page number buffer.
5457e32b20dSKelly Yancey 				 */
5467e32b20dSKelly Yancey 				state = LOCATE;
5477e32b20dSKelly Yancey 			} else if (! BUFFERFULL(str_pagename)) {
5487e32b20dSKelly Yancey 				strncat(str_pagename, &c, 1);
5497e32b20dSKelly Yancey 			} else {
550024ae004SRuslan Ermilov 				errx(EX_OSFILE, "%s:%d: %s %zd %s", pagedb_path,
5517e32b20dSKelly Yancey 				    lineno, "page name exceeds",
55254644e21SAlexander Motin 				    sizeof(str_page) - 1, "characters");
5537e32b20dSKelly Yancey 			}
5547e32b20dSKelly Yancey 			break;
5557e32b20dSKelly Yancey 
5567e32b20dSKelly Yancey 		case PAGEDEF:
5577e32b20dSKelly Yancey 			/*
5587e32b20dSKelly Yancey 			 * Transfer the page definition into a format buffer
5597e32b20dSKelly Yancey 			 * suitable for use with CDB encoding/decoding routines.
5607e32b20dSKelly Yancey 			 */
5617e32b20dSKelly Yancey 			if (depth == 0) {
5627e32b20dSKelly Yancey 				found = 1;
5637e32b20dSKelly Yancey 				SETSTATE_LOCATE;
5647e32b20dSKelly Yancey 			} else if (! BUFFERFULL(format)) {
5657e32b20dSKelly Yancey 				strncat(format, &c, 1);
5667e32b20dSKelly Yancey 			} else {
567024ae004SRuslan Ermilov 				errx(EX_OSFILE, "%s:%d: %s %zd %s", pagedb_path,
5687e32b20dSKelly Yancey 				    lineno, "page definition exceeds",
5697e32b20dSKelly Yancey 				    sizeof(format) - 1, "characters");
5707e32b20dSKelly Yancey 			}
5717e32b20dSKelly Yancey 			break;
5727e32b20dSKelly Yancey 
5737e32b20dSKelly Yancey 		default:
574b055b157SGarrett Wollman 			; /* NOTREACHED */
5757e32b20dSKelly Yancey 		}
5767e32b20dSKelly Yancey 
5777e32b20dSKelly Yancey 		/* Repeat processing loop with next character. */
5787e32b20dSKelly Yancey 	}
5797e32b20dSKelly Yancey 
5807e32b20dSKelly Yancey 	if (ferror(pagedb))
5817e32b20dSKelly Yancey 		err(EX_OSFILE, "%s", pagedb_path);
5827e32b20dSKelly Yancey 
5837e32b20dSKelly Yancey 	/* Close the SCSI page database. */
5847e32b20dSKelly Yancey 	fclose(pagedb);
5857e32b20dSKelly Yancey 
5867e32b20dSKelly Yancey 	if (!found)			/* Never found a matching page. */
5877e32b20dSKelly Yancey 		returnerr(ESRCH);
5887e32b20dSKelly Yancey 
5897e32b20dSKelly Yancey 	return (0);
5907e32b20dSKelly Yancey }
5917e32b20dSKelly Yancey 
5927e32b20dSKelly Yancey static void
editlist_populate(struct cam_device * device,int cdb_len,int dbd,int pc,int page,int subpage,int task_attr,int retries,int timeout)593e341cfd2SAlexander Motin editlist_populate(struct cam_device *device, int cdb_len, int dbd, int pc,
594e341cfd2SAlexander Motin     int page, int subpage, int task_attr, int retries, int timeout)
5957e32b20dSKelly Yancey {
596*efff068cSWarner Losh 	uint8_t data[MAX_DATA_SIZE];	/* Buffer to hold mode parameters. */
597*efff068cSWarner Losh 	uint8_t *mode_pars;		/* Pointer to modepage params. */
5987e32b20dSKelly Yancey 	struct scsi_mode_page_header *mph;
59954644e21SAlexander Motin 	struct scsi_mode_page_header_sp *mphsp;
60036289c33SAlexander Motin 	size_t len;
6017e32b20dSKelly Yancey 
6027e32b20dSKelly Yancey 	STAILQ_INIT(&editlist);
6037e32b20dSKelly Yancey 
6047e32b20dSKelly Yancey 	/* Fetch changeable values; use to build initial editlist. */
60509128776SAlexander Motin 	mode_sense(device, &cdb_len, dbd, 0, 1, page, subpage, task_attr,
60609128776SAlexander Motin 		   retries, timeout, data, sizeof(data));
6077e32b20dSKelly Yancey 
608e341cfd2SAlexander Motin 	if (cdb_len == 6) {
609e341cfd2SAlexander Motin 		struct scsi_mode_header_6 *mh =
610e341cfd2SAlexander Motin 		    (struct scsi_mode_header_6 *)data;
611e341cfd2SAlexander Motin 		mph = find_mode_page_6(mh);
612e341cfd2SAlexander Motin 	} else {
613e341cfd2SAlexander Motin 		struct scsi_mode_header_10 *mh =
614e341cfd2SAlexander Motin 		    (struct scsi_mode_header_10 *)data;
615e341cfd2SAlexander Motin 		mph = find_mode_page_10(mh);
616e341cfd2SAlexander Motin 	}
61754644e21SAlexander Motin 	if ((mph->page_code & SMPH_SPF) == 0) {
61854644e21SAlexander Motin 		mode_pars = (uint8_t *)(mph + 1);
61954644e21SAlexander Motin 		len = mph->page_length;
62054644e21SAlexander Motin 	} else {
62154644e21SAlexander Motin 		mphsp = (struct scsi_mode_page_header_sp *)mph;
62254644e21SAlexander Motin 		mode_pars = (uint8_t *)(mphsp + 1);
62354644e21SAlexander Motin 		len = scsi_2btoul(mphsp->page_length);
62454644e21SAlexander Motin 	}
62536289c33SAlexander Motin 	len = MIN(len, sizeof(data) - (mode_pars - data));
6267e32b20dSKelly Yancey 
6277e32b20dSKelly Yancey 	/* Decode the value data, creating edit_entries for each value. */
62854644e21SAlexander Motin 	buff_decode_visit(mode_pars, len, format, editentry_create, 0);
6297e32b20dSKelly Yancey 
6307e32b20dSKelly Yancey 	/* Fetch the current/saved values; use to set editentry values. */
63109128776SAlexander Motin 	mode_sense(device, &cdb_len, dbd, 0, pc, page, subpage, task_attr,
632e341cfd2SAlexander Motin 	    retries, timeout, data, sizeof(data));
63354644e21SAlexander Motin 	buff_decode_visit(mode_pars, len, format, editentry_update, 0);
6347e32b20dSKelly Yancey }
6357e32b20dSKelly Yancey 
6367e32b20dSKelly Yancey static void
editlist_populate_desc(struct cam_device * device,int cdb_len,int llbaa,int pc,int page,int subpage,int task_attr,int retries,int timeout)63709128776SAlexander Motin editlist_populate_desc(struct cam_device *device, int cdb_len, int llbaa, int pc,
63809128776SAlexander Motin     int page, int subpage, int task_attr, int retries, int timeout)
63909128776SAlexander Motin {
64009128776SAlexander Motin 	uint8_t data[MAX_DATA_SIZE];	/* Buffer to hold mode parameters. */
64109128776SAlexander Motin 	uint8_t *desc;			/* Pointer to block descriptor. */
64209128776SAlexander Motin 	char num[8];
64309128776SAlexander Motin 	struct sbuf sb;
64409128776SAlexander Motin 	size_t len;
64509128776SAlexander Motin 	u_int longlba, dlen, i;
64609128776SAlexander Motin 
64709128776SAlexander Motin 	STAILQ_INIT(&editlist);
64809128776SAlexander Motin 
64909128776SAlexander Motin 	/* Fetch the current/saved values. */
65009128776SAlexander Motin 	mode_sense(device, &cdb_len, 0, llbaa, pc, page, subpage, task_attr,
65109128776SAlexander Motin 	    retries, timeout, data, sizeof(data));
65209128776SAlexander Motin 
65309128776SAlexander Motin 	if (cdb_len == 6) {
65409128776SAlexander Motin 		struct scsi_mode_header_6 *mh =
65509128776SAlexander Motin 		    (struct scsi_mode_header_6 *)data;
65609128776SAlexander Motin 		desc = (uint8_t *)(mh + 1);
65709128776SAlexander Motin 		len = mh->blk_desc_len;
65809128776SAlexander Motin 		longlba = 0;
65909128776SAlexander Motin 	} else {
66009128776SAlexander Motin 		struct scsi_mode_header_10 *mh =
66109128776SAlexander Motin 		    (struct scsi_mode_header_10 *)data;
66209128776SAlexander Motin 		desc = (uint8_t *)(mh + 1);
66309128776SAlexander Motin 		len = scsi_2btoul(mh->blk_desc_len);
66409128776SAlexander Motin 		longlba = (mh->flags & SMH_LONGLBA) != 0;
66509128776SAlexander Motin 	}
66609128776SAlexander Motin 	dlen = longlba ? 16 : 8;
66709128776SAlexander Motin 	len = MIN(len, sizeof(data) - (desc - data));
66809128776SAlexander Motin 
66909128776SAlexander Motin 	sbuf_new(&sb, format, sizeof(format), SBUF_FIXEDLEN);
67009128776SAlexander Motin 	num[0] = 0;
67109128776SAlexander Motin 	for (i = 0; i * dlen < len; i++) {
67209128776SAlexander Motin 		if (i > 0)
67309128776SAlexander Motin 			snprintf(num, sizeof(num), " %d", i + 1);
67409128776SAlexander Motin 		if (longlba) {
67509128776SAlexander Motin 			sbuf_printf(&sb, "{Number of Logical Blocks%s High} i4\n", num);
67609128776SAlexander Motin 			sbuf_printf(&sb, "{Number of Logical Blocks%s} i4\n", num);
67709128776SAlexander Motin 			sbuf_cat(&sb, "{Reserved} *i4\n");
67809128776SAlexander Motin 			sbuf_printf(&sb, "{Logical Block Length%s} i4\n", num);
67909128776SAlexander Motin 		} else if (device->pd_type == T_DIRECT) {
68009128776SAlexander Motin 			sbuf_printf(&sb, "{Number of Logical Blocks%s} i4\n", num);
68109128776SAlexander Motin 			sbuf_cat(&sb, "{Reserved} *i1\n");
68209128776SAlexander Motin 			sbuf_printf(&sb, "{Logical Block Length%s} i3\n", num);
68309128776SAlexander Motin 		} else {
68409128776SAlexander Motin 			sbuf_printf(&sb, "{Density Code%s} i1\n", num);
68509128776SAlexander Motin 			sbuf_printf(&sb, "{Number of Logical Blocks%s} i3\n", num);
68609128776SAlexander Motin 			sbuf_cat(&sb, "{Reserved} *i1\n");
68709128776SAlexander Motin 			sbuf_printf(&sb, "{Logical Block Length%s} i3\n", num);
68809128776SAlexander Motin 		}
68909128776SAlexander Motin 	}
69009128776SAlexander Motin 	sbuf_finish(&sb);
69109128776SAlexander Motin 	sbuf_delete(&sb);
69209128776SAlexander Motin 
69309128776SAlexander Motin 	/* Decode the value data, creating edit_entries for each value. */
69409128776SAlexander Motin 	buff_decode_visit(desc, len, format, editentry_create_desc, 0);
69509128776SAlexander Motin }
69609128776SAlexander Motin 
69709128776SAlexander Motin static void
editlist_save(struct cam_device * device,int cdb_len,int dbd,int pc,int page,int subpage,int task_attr,int retries,int timeout)698e341cfd2SAlexander Motin editlist_save(struct cam_device *device, int cdb_len, int dbd, int pc,
699e341cfd2SAlexander Motin     int page, int subpage, int task_attr, int retries, int timeout)
7007e32b20dSKelly Yancey {
701*efff068cSWarner Losh 	uint8_t data[MAX_DATA_SIZE];	/* Buffer to hold mode parameters. */
702*efff068cSWarner Losh 	uint8_t *mode_pars;		/* Pointer to modepage params. */
7037e32b20dSKelly Yancey 	struct scsi_mode_page_header *mph;
70454644e21SAlexander Motin 	struct scsi_mode_page_header_sp *mphsp;
705e341cfd2SAlexander Motin 	size_t len, hlen, mphlen;
7067e32b20dSKelly Yancey 
7077e32b20dSKelly Yancey 	/* Make sure that something changed before continuing. */
7087e32b20dSKelly Yancey 	if (! editlist_changed)
7097e32b20dSKelly Yancey 		return;
7107e32b20dSKelly Yancey 
71154644e21SAlexander Motin 	/* Preload the CDB buffer with the current mode page data. */
71209128776SAlexander Motin 	mode_sense(device, &cdb_len, dbd, 0, pc, page, subpage, task_attr,
713e341cfd2SAlexander Motin 	    retries, timeout, data, sizeof(data));
7147e32b20dSKelly Yancey 
7157e32b20dSKelly Yancey 	/* Initial headers & offsets. */
7161e773aebSKenneth D. Merry 	/*
7171e773aebSKenneth D. Merry 	 * Tape drives include write protect (WP), Buffered Mode and Speed
7181e773aebSKenneth D. Merry 	 * settings in the device-specific parameter.  Clearing this
7191e773aebSKenneth D. Merry 	 * parameter on a mode select can have the effect of turning off
7201e773aebSKenneth D. Merry 	 * write protect or buffered mode, or changing the speed setting of
7211e773aebSKenneth D. Merry 	 * the tape drive.
7221e773aebSKenneth D. Merry 	 *
7231e773aebSKenneth D. Merry 	 * Disks report DPO/FUA support via the device specific parameter
7241e773aebSKenneth D. Merry 	 * for MODE SENSE, but the bit is reserved for MODE SELECT.  So we
7251e773aebSKenneth D. Merry 	 * clear this for disks (and other non-tape devices) to avoid
7261e773aebSKenneth D. Merry 	 * potential errors from the target device.
7271e773aebSKenneth D. Merry 	 */
728e341cfd2SAlexander Motin 	if (cdb_len == 6) {
729e341cfd2SAlexander Motin 		struct scsi_mode_header_6 *mh =
730e341cfd2SAlexander Motin 		    (struct scsi_mode_header_6 *)data;
731e341cfd2SAlexander Motin 		hlen = sizeof(*mh);
732e341cfd2SAlexander Motin 		/* Eliminate block descriptors. */
733e341cfd2SAlexander Motin 		if (mh->blk_desc_len > 0) {
734e341cfd2SAlexander Motin 			bcopy(find_mode_page_6(mh), mh + 1,
735e341cfd2SAlexander Motin 			    mh->data_length + 1 - hlen -
736e341cfd2SAlexander Motin 			    mh->blk_desc_len);
737e341cfd2SAlexander Motin 			mh->blk_desc_len = 0;
738e341cfd2SAlexander Motin 		}
739e341cfd2SAlexander Motin 		mh->data_length = 0;	/* Reserved for MODE SELECT command. */
7401e773aebSKenneth D. Merry 		if (device->pd_type != T_SEQUENTIAL)
741e341cfd2SAlexander Motin 			mh->dev_spec = 0;	/* See comment above */
742e341cfd2SAlexander Motin 		mph = find_mode_page_6(mh);
743e341cfd2SAlexander Motin 	} else {
744e341cfd2SAlexander Motin 		struct scsi_mode_header_10 *mh =
745e341cfd2SAlexander Motin 		    (struct scsi_mode_header_10 *)data;
746e341cfd2SAlexander Motin 		hlen = sizeof(*mh);
747e341cfd2SAlexander Motin 		/* Eliminate block descriptors. */
748e341cfd2SAlexander Motin 		if (scsi_2btoul(mh->blk_desc_len) > 0) {
749e341cfd2SAlexander Motin 			bcopy(find_mode_page_10(mh), mh + 1,
750e341cfd2SAlexander Motin 			    scsi_2btoul(mh->data_length) + 1 - hlen -
751e341cfd2SAlexander Motin 			    scsi_2btoul(mh->blk_desc_len));
752e341cfd2SAlexander Motin 			scsi_ulto2b(0, mh->blk_desc_len);
753e341cfd2SAlexander Motin 		}
754e341cfd2SAlexander Motin 		scsi_ulto2b(0, mh->data_length); /* Reserved for MODE SELECT. */
755e341cfd2SAlexander Motin 		if (device->pd_type != T_SEQUENTIAL)
756e341cfd2SAlexander Motin 			mh->dev_spec = 0;	/* See comment above */
757e341cfd2SAlexander Motin 		mph = find_mode_page_10(mh);
758e341cfd2SAlexander Motin 	}
759e341cfd2SAlexander Motin 	if ((mph->page_code & SMPH_SPF) == 0) {
760e341cfd2SAlexander Motin 		mphlen = sizeof(*mph);
761e341cfd2SAlexander Motin 		mode_pars = (uint8_t *)(mph + 1);
762e341cfd2SAlexander Motin 		len = mph->page_length;
763e341cfd2SAlexander Motin 	} else {
764e341cfd2SAlexander Motin 		mphsp = (struct scsi_mode_page_header_sp *)mph;
765e341cfd2SAlexander Motin 		mphlen = sizeof(*mphsp);
766e341cfd2SAlexander Motin 		mode_pars = (uint8_t *)(mphsp + 1);
767e341cfd2SAlexander Motin 		len = scsi_2btoul(mphsp->page_length);
768e341cfd2SAlexander Motin 	}
769e341cfd2SAlexander Motin 	len = MIN(len, sizeof(data) - (mode_pars - data));
770e341cfd2SAlexander Motin 
771e341cfd2SAlexander Motin 	/* Encode the value data to be passed back to the device. */
772e341cfd2SAlexander Motin 	buff_encode_visit(mode_pars, len, format, editentry_save, 0);
773e341cfd2SAlexander Motin 
77454644e21SAlexander Motin 	mph->page_code &= ~SMPH_PS;	/* Reserved for MODE SELECT command. */
7757e32b20dSKelly Yancey 
7767e32b20dSKelly Yancey 	/*
7777e32b20dSKelly Yancey 	 * Write the changes back to the device. If the user editted control
7787e32b20dSKelly Yancey 	 * page 3 (saved values) then request the changes be permanently
7797e32b20dSKelly Yancey 	 * recorded.
7807e32b20dSKelly Yancey 	 */
781e341cfd2SAlexander Motin 	mode_select(device, cdb_len, (pc << PAGE_CTRL_SHIFT == SMS_PAGE_CTRL_SAVED),
782e341cfd2SAlexander Motin 	    task_attr, retries, timeout, data, hlen + mphlen + len);
7837e32b20dSKelly Yancey }
7847e32b20dSKelly Yancey 
78509128776SAlexander Motin static void
editlist_save_desc(struct cam_device * device,int cdb_len,int llbaa,int pc,int page,int subpage,int task_attr,int retries,int timeout)78609128776SAlexander Motin editlist_save_desc(struct cam_device *device, int cdb_len, int llbaa, int pc,
78709128776SAlexander Motin     int page, int subpage, int task_attr, int retries, int timeout)
78809128776SAlexander Motin {
78909128776SAlexander Motin 	uint8_t data[MAX_DATA_SIZE];	/* Buffer to hold mode parameters. */
79009128776SAlexander Motin 	uint8_t *desc;			/* Pointer to block descriptor. */
79109128776SAlexander Motin 	size_t len, hlen;
79209128776SAlexander Motin 
79309128776SAlexander Motin 	/* Make sure that something changed before continuing. */
79409128776SAlexander Motin 	if (! editlist_changed)
79509128776SAlexander Motin 		return;
79609128776SAlexander Motin 
79709128776SAlexander Motin 	/* Preload the CDB buffer with the current mode page data. */
79809128776SAlexander Motin 	mode_sense(device, &cdb_len, 0, llbaa, pc, page, subpage, task_attr,
79909128776SAlexander Motin 	    retries, timeout, data, sizeof(data));
80009128776SAlexander Motin 
80109128776SAlexander Motin 	/* Initial headers & offsets. */
80209128776SAlexander Motin 	if (cdb_len == 6) {
80309128776SAlexander Motin 		struct scsi_mode_header_6 *mh =
80409128776SAlexander Motin 		    (struct scsi_mode_header_6 *)data;
80509128776SAlexander Motin 		hlen = sizeof(*mh);
80609128776SAlexander Motin 		desc = (uint8_t *)(mh + 1);
80709128776SAlexander Motin 		len = mh->blk_desc_len;
80809128776SAlexander Motin 		mh->data_length = 0;	/* Reserved for MODE SELECT command. */
80909128776SAlexander Motin 		if (device->pd_type != T_SEQUENTIAL)
81009128776SAlexander Motin 			mh->dev_spec = 0;	/* See comment above */
81109128776SAlexander Motin 	} else {
81209128776SAlexander Motin 		struct scsi_mode_header_10 *mh =
81309128776SAlexander Motin 		    (struct scsi_mode_header_10 *)data;
81409128776SAlexander Motin 		hlen = sizeof(*mh);
81509128776SAlexander Motin 		desc = (uint8_t *)(mh + 1);
81609128776SAlexander Motin 		len = scsi_2btoul(mh->blk_desc_len);
81709128776SAlexander Motin 		scsi_ulto2b(0, mh->data_length); /* Reserved for MODE SELECT. */
81809128776SAlexander Motin 		if (device->pd_type != T_SEQUENTIAL)
81909128776SAlexander Motin 			mh->dev_spec = 0;	/* See comment above */
82009128776SAlexander Motin 	}
82109128776SAlexander Motin 	len = MIN(len, sizeof(data) - (desc - data));
82209128776SAlexander Motin 
82309128776SAlexander Motin 	/* Encode the value data to be passed back to the device. */
82409128776SAlexander Motin 	buff_encode_visit(desc, len, format, editentry_save, 0);
82509128776SAlexander Motin 
82609128776SAlexander Motin 	/*
82709128776SAlexander Motin 	 * Write the changes back to the device. If the user editted control
82809128776SAlexander Motin 	 * page 3 (saved values) then request the changes be permanently
82909128776SAlexander Motin 	 * recorded.
83009128776SAlexander Motin 	 */
83109128776SAlexander Motin 	mode_select(device, cdb_len, (pc << PAGE_CTRL_SHIFT == SMS_PAGE_CTRL_SAVED),
83209128776SAlexander Motin 	    task_attr, retries, timeout, data, hlen + len);
83309128776SAlexander Motin }
83409128776SAlexander Motin 
8357e32b20dSKelly Yancey static int
modepage_write(FILE * file,int editonly)8367e32b20dSKelly Yancey modepage_write(FILE *file, int editonly)
8377e32b20dSKelly Yancey {
8387e32b20dSKelly Yancey 	struct editentry *scan;
8397e32b20dSKelly Yancey 	int written = 0;
8407e32b20dSKelly Yancey 
8417e32b20dSKelly Yancey 	STAILQ_FOREACH(scan, &editlist, link) {
8427e32b20dSKelly Yancey 		if (scan->editable || !editonly) {
8437e32b20dSKelly Yancey 			written++;
8447e32b20dSKelly Yancey 			if (scan->type == 'c' || scan->type == 'z') {
8457e32b20dSKelly Yancey 				fprintf(file, "%s:  %s\n", scan->name,
8467e32b20dSKelly Yancey 				    scan->value.svalue);
8477e32b20dSKelly Yancey 			} else {
84809128776SAlexander Motin 				fprintf(file, "%s:  %u\n", scan->name,
8497e32b20dSKelly Yancey 				    scan->value.ivalue);
8507e32b20dSKelly Yancey 			}
8517e32b20dSKelly Yancey 		}
8527e32b20dSKelly Yancey 	}
8537e32b20dSKelly Yancey 	return (written);
8547e32b20dSKelly Yancey }
8557e32b20dSKelly Yancey 
8567e32b20dSKelly Yancey static int
modepage_read(FILE * file)8577e32b20dSKelly Yancey modepage_read(FILE *file)
8587e32b20dSKelly Yancey {
8597e32b20dSKelly Yancey 	char *buffer;			/* Pointer to dynamic line buffer.  */
8607e32b20dSKelly Yancey 	char *line;			/* Pointer to static fgetln buffer. */
8617e32b20dSKelly Yancey 	char *name;			/* Name portion of the line buffer. */
8627e32b20dSKelly Yancey 	char *value;			/* Value portion of line buffer.    */
863c7cf7aa6SJohan Karlsson 	size_t length;			/* Length of static fgetln buffer.  */
8647e32b20dSKelly Yancey 
8657e32b20dSKelly Yancey #define	ABORT_READ(message, param) do {					\
8667e32b20dSKelly Yancey 	warnx(message, param);						\
8677e32b20dSKelly Yancey 	free(buffer);							\
8687e32b20dSKelly Yancey 	returnerr(EAGAIN);						\
8697e32b20dSKelly Yancey } while (0)
8707e32b20dSKelly Yancey 
8717e32b20dSKelly Yancey 	while ((line = fgetln(file, &length)) != NULL) {
8727e32b20dSKelly Yancey 		/* Trim trailing whitespace (including optional newline). */
8737e32b20dSKelly Yancey 		while (length > 0 && isspace(line[length - 1]))
8747e32b20dSKelly Yancey 			length--;
8757e32b20dSKelly Yancey 
8767e32b20dSKelly Yancey 	    	/* Allocate a buffer to hold the line + terminating null. */
8777e32b20dSKelly Yancey 	    	if ((buffer = malloc(length + 1)) == NULL)
8787e32b20dSKelly Yancey 			err(EX_OSERR, NULL);
8797e32b20dSKelly Yancey 		memcpy(buffer, line, length);
8807e32b20dSKelly Yancey 		buffer[length] = '\0';
8817e32b20dSKelly Yancey 
8827e32b20dSKelly Yancey 		/* Strip out comments. */
8837e32b20dSKelly Yancey 		if ((value = strchr(buffer, '#')) != NULL)
8847e32b20dSKelly Yancey 			*value = '\0';
8857e32b20dSKelly Yancey 
8867e32b20dSKelly Yancey 		/* The name is first in the buffer. Trim whitespace.*/
8877e32b20dSKelly Yancey 		name = buffer;
8887e32b20dSKelly Yancey 		RTRIM(name);
8897e32b20dSKelly Yancey 		while (isspace(*name))
8907e32b20dSKelly Yancey 			name++;
8917e32b20dSKelly Yancey 
8927e32b20dSKelly Yancey 		/* Skip empty lines. */
8937e32b20dSKelly Yancey 		if (strlen(name) == 0)
8947e32b20dSKelly Yancey 			continue;
8957e32b20dSKelly Yancey 
8967e32b20dSKelly Yancey 		/* The name ends at the colon; the value starts there. */
8977e32b20dSKelly Yancey 		if ((value = strrchr(buffer, ':')) == NULL)
8987e32b20dSKelly Yancey 			ABORT_READ("no value associated with %s", name);
8997e32b20dSKelly Yancey 		*value = '\0';			/* Null-terminate name. */
9007e32b20dSKelly Yancey 		value++;			/* Value starts afterwards. */
9017e32b20dSKelly Yancey 
9027e32b20dSKelly Yancey 		/* Trim leading and trailing whitespace. */
9037e32b20dSKelly Yancey 		RTRIM(value);
9047e32b20dSKelly Yancey 		while (isspace(*value))
9057e32b20dSKelly Yancey 			value++;
9067e32b20dSKelly Yancey 
9077e32b20dSKelly Yancey 		/* Make sure there is a value left. */
9087e32b20dSKelly Yancey 		if (strlen(value) == 0)
9097e32b20dSKelly Yancey 			ABORT_READ("no value associated with %s", name);
9107e32b20dSKelly Yancey 
9117e32b20dSKelly Yancey 		/* Update our in-memory copy of the modepage entry value. */
9127e32b20dSKelly Yancey 		if (editentry_set(name, value, 1) != 0) {
9137e32b20dSKelly Yancey 			if (errno == ENOENT) {
9147e32b20dSKelly Yancey 				/* No entry by the name. */
9157e32b20dSKelly Yancey 				ABORT_READ("no such modepage entry \"%s\"",
9167e32b20dSKelly Yancey 				    name);
9177e32b20dSKelly Yancey 			} else if (errno == EINVAL) {
9187e32b20dSKelly Yancey 				/* Invalid value. */
9197e32b20dSKelly Yancey 				ABORT_READ("Invalid value for entry \"%s\"",
9207e32b20dSKelly Yancey 				    name);
9217e32b20dSKelly Yancey 			} else if (errno == ERANGE) {
9227e32b20dSKelly Yancey 				/* Value out of range for entry type. */
9237e32b20dSKelly Yancey 				ABORT_READ("value out of range for %s", name);
9247e32b20dSKelly Yancey 			} else if (errno == EPERM) {
9257e32b20dSKelly Yancey 				/* Entry is not editable; not fatal. */
9267e32b20dSKelly Yancey 				warnx("modepage entry \"%s\" is read-only; "
9277e32b20dSKelly Yancey 				    "skipping.", name);
9287e32b20dSKelly Yancey 			}
9297e32b20dSKelly Yancey 		}
9307e32b20dSKelly Yancey 
9317e32b20dSKelly Yancey 		free(buffer);
9327e32b20dSKelly Yancey 	}
9337e32b20dSKelly Yancey 	return (ferror(file)? -1: 0);
9347e32b20dSKelly Yancey 
9357e32b20dSKelly Yancey #undef ABORT_READ
9367e32b20dSKelly Yancey }
9377e32b20dSKelly Yancey 
9387e32b20dSKelly Yancey static void
modepage_edit(void)9397e32b20dSKelly Yancey modepage_edit(void)
9407e32b20dSKelly Yancey {
94139867613SJohan Karlsson 	const char *editor;
9427e32b20dSKelly Yancey 	char *commandline;
9437e32b20dSKelly Yancey 	int fd;
9447e32b20dSKelly Yancey 	int written;
9457e32b20dSKelly Yancey 
9467e32b20dSKelly Yancey 	if (!isatty(fileno(stdin))) {
9477e32b20dSKelly Yancey 		/* Not a tty, read changes from stdin. */
9487e32b20dSKelly Yancey 		modepage_read(stdin);
9497e32b20dSKelly Yancey 		return;
9507e32b20dSKelly Yancey 	}
9517e32b20dSKelly Yancey 
9527e32b20dSKelly Yancey 	/* Lookup editor to invoke. */
9537e32b20dSKelly Yancey 	if ((editor = getenv("EDITOR")) == NULL)
9547e32b20dSKelly Yancey 		editor = DEFAULT_EDITOR;
9557e32b20dSKelly Yancey 
9567e32b20dSKelly Yancey 	/* Create temp file for editor to modify. */
9577e32b20dSKelly Yancey 	if ((fd = mkstemp(edit_path)) == -1)
9587e32b20dSKelly Yancey 		errx(EX_CANTCREAT, "mkstemp failed");
9597e32b20dSKelly Yancey 
9607e32b20dSKelly Yancey 	atexit(cleanup_editfile);
9617e32b20dSKelly Yancey 
9627e32b20dSKelly Yancey 	if ((edit_file = fdopen(fd, "w")) == NULL)
9637e32b20dSKelly Yancey 		err(EX_NOINPUT, "%s", edit_path);
9647e32b20dSKelly Yancey 
9657e32b20dSKelly Yancey 	written = modepage_write(edit_file, 1);
966525689f1SJustin T. Gibbs 
967525689f1SJustin T. Gibbs 	fclose(edit_file);
9687e32b20dSKelly Yancey 	edit_file = NULL;
969525689f1SJustin T. Gibbs 
9707e32b20dSKelly Yancey 	if (written == 0) {
9717e32b20dSKelly Yancey 		warnx("no editable entries");
9727e32b20dSKelly Yancey 		cleanup_editfile();
9737e32b20dSKelly Yancey 		return;
9747e32b20dSKelly Yancey 	}
975525689f1SJustin T. Gibbs 
9767e32b20dSKelly Yancey 	/*
9777e32b20dSKelly Yancey 	 * Allocate memory to hold the command line (the 2 extra characters
9787e32b20dSKelly Yancey 	 * are to hold the argument separator (a space), and the terminating
9797e32b20dSKelly Yancey 	 * null character.
9807e32b20dSKelly Yancey 	 */
9817e32b20dSKelly Yancey 	commandline = malloc(strlen(editor) + strlen(edit_path) + 2);
9827e32b20dSKelly Yancey 	if (commandline == NULL)
9837e32b20dSKelly Yancey 		err(EX_OSERR, NULL);
9847e32b20dSKelly Yancey 	sprintf(commandline, "%s %s", editor, edit_path);
9857e32b20dSKelly Yancey 
9867e32b20dSKelly Yancey 	/* Invoke the editor on the temp file. */
9877e32b20dSKelly Yancey 	if (system(commandline) == -1)
9887e32b20dSKelly Yancey 		err(EX_UNAVAILABLE, "could not invoke %s", editor);
9897e32b20dSKelly Yancey 	free(commandline);
9907e32b20dSKelly Yancey 
9917e32b20dSKelly Yancey 	if ((edit_file = fopen(edit_path, "r")) == NULL)
9927e32b20dSKelly Yancey 		err(EX_NOINPUT, "%s", edit_path);
9937e32b20dSKelly Yancey 
9947e32b20dSKelly Yancey 	/* Read any changes made to the temp file. */
9957e32b20dSKelly Yancey 	modepage_read(edit_file);
9967e32b20dSKelly Yancey 
9977e32b20dSKelly Yancey 	cleanup_editfile();
9987e32b20dSKelly Yancey }
9997e32b20dSKelly Yancey 
10007e32b20dSKelly Yancey static void
modepage_dump(struct cam_device * device,int cdb_len,int dbd,int pc,int page,int subpage,int task_attr,int retries,int timeout)1001e341cfd2SAlexander Motin modepage_dump(struct cam_device *device, int cdb_len, int dbd, int pc,
1002e341cfd2SAlexander Motin 	      int page, int subpage, int task_attr, int retries, int timeout)
10037e32b20dSKelly Yancey {
1004*efff068cSWarner Losh 	uint8_t data[MAX_DATA_SIZE];	/* Buffer to hold mode parameters. */
1005*efff068cSWarner Losh 	uint8_t *mode_pars;		/* Pointer to modepage params. */
10067e32b20dSKelly Yancey 	struct scsi_mode_page_header *mph;
100754644e21SAlexander Motin 	struct scsi_mode_page_header_sp *mphsp;
100836289c33SAlexander Motin 	size_t indx, len;
10097e32b20dSKelly Yancey 
101009128776SAlexander Motin 	mode_sense(device, &cdb_len, dbd, 0, pc, page, subpage, task_attr,
1011e341cfd2SAlexander Motin 	    retries, timeout, data, sizeof(data));
10127e32b20dSKelly Yancey 
1013e341cfd2SAlexander Motin 	if (cdb_len == 6) {
1014e341cfd2SAlexander Motin 		struct scsi_mode_header_6 *mh =
1015e341cfd2SAlexander Motin 		    (struct scsi_mode_header_6 *)data;
1016e341cfd2SAlexander Motin 		mph = find_mode_page_6(mh);
1017e341cfd2SAlexander Motin 	} else {
1018e341cfd2SAlexander Motin 		struct scsi_mode_header_10 *mh =
1019e341cfd2SAlexander Motin 		    (struct scsi_mode_header_10 *)data;
1020e341cfd2SAlexander Motin 		mph = find_mode_page_10(mh);
1021e341cfd2SAlexander Motin 	}
102254644e21SAlexander Motin 	if ((mph->page_code & SMPH_SPF) == 0) {
102354644e21SAlexander Motin 		mode_pars = (uint8_t *)(mph + 1);
102454644e21SAlexander Motin 		len = mph->page_length;
102554644e21SAlexander Motin 	} else {
102654644e21SAlexander Motin 		mphsp = (struct scsi_mode_page_header_sp *)mph;
102754644e21SAlexander Motin 		mode_pars = (uint8_t *)(mphsp + 1);
102854644e21SAlexander Motin 		len = scsi_2btoul(mphsp->page_length);
102954644e21SAlexander Motin 	}
103036289c33SAlexander Motin 	len = MIN(len, sizeof(data) - (mode_pars - data));
10317e32b20dSKelly Yancey 
10327e32b20dSKelly Yancey 	/* Print the raw mode page data with newlines each 8 bytes. */
103354644e21SAlexander Motin 	for (indx = 0; indx < len; indx++) {
103439867613SJohan Karlsson 		printf("%02x%c",mode_pars[indx],
103539867613SJohan Karlsson 		    (((indx + 1) % 8) == 0) ? '\n' : ' ');
10367e32b20dSKelly Yancey 	}
10377e32b20dSKelly Yancey 	putchar('\n');
10387e32b20dSKelly Yancey }
103909128776SAlexander Motin static void
modepage_dump_desc(struct cam_device * device,int cdb_len,int llbaa,int pc,int page,int subpage,int task_attr,int retries,int timeout)104009128776SAlexander Motin modepage_dump_desc(struct cam_device *device, int cdb_len, int llbaa, int pc,
104109128776SAlexander Motin 	      int page, int subpage, int task_attr, int retries, int timeout)
104209128776SAlexander Motin {
104309128776SAlexander Motin 	uint8_t data[MAX_DATA_SIZE];	/* Buffer to hold mode parameters. */
104409128776SAlexander Motin 	uint8_t *desc;			/* Pointer to block descriptor. */
104509128776SAlexander Motin 	size_t indx, len;
104609128776SAlexander Motin 
104709128776SAlexander Motin 	mode_sense(device, &cdb_len, 0, llbaa, pc, page, subpage, task_attr,
104809128776SAlexander Motin 	    retries, timeout, data, sizeof(data));
104909128776SAlexander Motin 
105009128776SAlexander Motin 	if (cdb_len == 6) {
105109128776SAlexander Motin 		struct scsi_mode_header_6 *mh =
105209128776SAlexander Motin 		    (struct scsi_mode_header_6 *)data;
105309128776SAlexander Motin 		desc = (uint8_t *)(mh + 1);
105409128776SAlexander Motin 		len = mh->blk_desc_len;
105509128776SAlexander Motin 	} else {
105609128776SAlexander Motin 		struct scsi_mode_header_10 *mh =
105709128776SAlexander Motin 		    (struct scsi_mode_header_10 *)data;
105809128776SAlexander Motin 		desc = (uint8_t *)(mh + 1);
105909128776SAlexander Motin 		len = scsi_2btoul(mh->blk_desc_len);
106009128776SAlexander Motin 	}
106109128776SAlexander Motin 	len = MIN(len, sizeof(data) - (desc - data));
106209128776SAlexander Motin 
106309128776SAlexander Motin 	/* Print the raw mode page data with newlines each 8 bytes. */
106409128776SAlexander Motin 	for (indx = 0; indx < len; indx++) {
106509128776SAlexander Motin 		printf("%02x%c", desc[indx],
106609128776SAlexander Motin 		    (((indx + 1) % 8) == 0) ? '\n' : ' ');
106709128776SAlexander Motin 	}
106809128776SAlexander Motin 	putchar('\n');
106909128776SAlexander Motin }
10707e32b20dSKelly Yancey 
10717e32b20dSKelly Yancey static void
cleanup_editfile(void)10727e32b20dSKelly Yancey cleanup_editfile(void)
10737e32b20dSKelly Yancey {
10747e32b20dSKelly Yancey 	if (edit_file == NULL)
10757e32b20dSKelly Yancey 		return;
10767e32b20dSKelly Yancey 	if (fclose(edit_file) != 0 || unlink(edit_path) != 0)
10777e32b20dSKelly Yancey 		warn("%s", edit_path);
10787e32b20dSKelly Yancey 	edit_file = NULL;
1079525689f1SJustin T. Gibbs }
1080525689f1SJustin T. Gibbs 
1081525689f1SJustin T. Gibbs void
mode_edit(struct cam_device * device,int cdb_len,int desc,int dbd,int llbaa,int pc,int page,int subpage,int edit,int binary,int task_attr,int retry_count,int timeout)108209128776SAlexander Motin mode_edit(struct cam_device *device, int cdb_len, int desc, int dbd, int llbaa,
108309128776SAlexander Motin     int pc, int page, int subpage, int edit, int binary, int task_attr,
108409128776SAlexander Motin     int retry_count, int timeout)
1085525689f1SJustin T. Gibbs {
108639867613SJohan Karlsson 	const char *pagedb_path;	/* Path to modepage database. */
1087525689f1SJustin T. Gibbs 
108809128776SAlexander Motin 	if (binary) {
108909128776SAlexander Motin 		if (edit)
10907e32b20dSKelly Yancey 			errx(EX_USAGE, "cannot edit in binary mode.");
109109128776SAlexander Motin 	} else if (desc) {
109209128776SAlexander Motin 		editlist_populate_desc(device, cdb_len, llbaa, pc, page,
109309128776SAlexander Motin 		    subpage, task_attr, retry_count, timeout);
109409128776SAlexander Motin 	} else {
10957e32b20dSKelly Yancey 		if ((pagedb_path = getenv("SCSI_MODES")) == NULL)
10967e32b20dSKelly Yancey 			pagedb_path = DEFAULT_SCSI_MODE_DB;
1097525689f1SJustin T. Gibbs 
109854644e21SAlexander Motin 		if (load_format(pagedb_path, page, subpage) != 0 &&
109954644e21SAlexander Motin 		    (edit || verbose)) {
11007e32b20dSKelly Yancey 			if (errno == ENOENT) {
11017e32b20dSKelly Yancey 				/* Modepage database file not found. */
11027e32b20dSKelly Yancey 				warn("cannot open modepage database \"%s\"",
11037e32b20dSKelly Yancey 				    pagedb_path);
11047e32b20dSKelly Yancey 			} else if (errno == ESRCH) {
11057e32b20dSKelly Yancey 				/* Modepage entry not found in database. */
110654644e21SAlexander Motin 				warnx("modepage 0x%02x,0x%02x not found in "
110754644e21SAlexander Motin 				    "database \"%s\"", page, subpage,
110854644e21SAlexander Motin 				    pagedb_path);
11097e32b20dSKelly Yancey 			}
11107e32b20dSKelly Yancey 			/* We can recover in display mode, otherwise we exit. */
11117e32b20dSKelly Yancey 			if (!edit) {
11127e32b20dSKelly Yancey 				warnx("reverting to binary display only");
11137e32b20dSKelly Yancey 				binary = 1;
11147e32b20dSKelly Yancey 			} else
11157e32b20dSKelly Yancey 				exit(EX_OSFILE);
11167e32b20dSKelly Yancey 		}
11177e32b20dSKelly Yancey 
1118e341cfd2SAlexander Motin 		editlist_populate(device, cdb_len, dbd, pc, page, subpage,
1119e341cfd2SAlexander Motin 		    task_attr, retry_count, timeout);
1120525689f1SJustin T. Gibbs 	}
1121525689f1SJustin T. Gibbs 
1122525689f1SJustin T. Gibbs 	if (edit) {
112354644e21SAlexander Motin 		if (pc << PAGE_CTRL_SHIFT != SMS_PAGE_CTRL_CURRENT &&
112454644e21SAlexander Motin 		    pc << PAGE_CTRL_SHIFT != SMS_PAGE_CTRL_SAVED)
11257e32b20dSKelly Yancey 			errx(EX_USAGE, "it only makes sense to edit page 0 "
1126525689f1SJustin T. Gibbs 			    "(current) or page 3 (saved values)");
11277e32b20dSKelly Yancey 		modepage_edit();
112809128776SAlexander Motin 		if (desc) {
112909128776SAlexander Motin 			editlist_save_desc(device, cdb_len, llbaa, pc, page,
113009128776SAlexander Motin 			    subpage, task_attr, retry_count, timeout);
113109128776SAlexander Motin 		} else {
1132e341cfd2SAlexander Motin 			editlist_save(device, cdb_len, dbd, pc, page, subpage,
1133e341cfd2SAlexander Motin 			    task_attr, retry_count, timeout);
113409128776SAlexander Motin 		}
11357e32b20dSKelly Yancey 	} else if (binary || STAILQ_EMPTY(&editlist)) {
11367e32b20dSKelly Yancey 		/* Display without formatting information. */
113709128776SAlexander Motin 		if (desc) {
113809128776SAlexander Motin 			modepage_dump_desc(device, cdb_len, llbaa, pc, page,
113909128776SAlexander Motin 			    subpage, task_attr, retry_count, timeout);
114009128776SAlexander Motin 		} else {
1141e341cfd2SAlexander Motin 			modepage_dump(device, cdb_len, dbd, pc, page, subpage,
1142e341cfd2SAlexander Motin 			    task_attr, retry_count, timeout);
114309128776SAlexander Motin 		}
1144525689f1SJustin T. Gibbs 	} else {
11457e32b20dSKelly Yancey 		/* Display with format. */
11467e32b20dSKelly Yancey 		modepage_write(stdout, 0);
11477e32b20dSKelly Yancey 	}
11487e32b20dSKelly Yancey }
11497e32b20dSKelly Yancey 
11507e32b20dSKelly Yancey void
mode_list(struct cam_device * device,int cdb_len,int dbd,int pc,int subpages,int task_attr,int retry_count,int timeout)1151e341cfd2SAlexander Motin mode_list(struct cam_device *device, int cdb_len, int dbd, int pc, int subpages,
1152492a2ef5SKenneth D. Merry 	  int task_attr, int retry_count, int timeout)
11537e32b20dSKelly Yancey {
1154*efff068cSWarner Losh 	uint8_t data[MAX_DATA_SIZE];	/* Buffer to hold mode parameters. */
11557e32b20dSKelly Yancey 	struct scsi_mode_page_header *mph;
115654644e21SAlexander Motin 	struct scsi_mode_page_header_sp *mphsp;
11577e32b20dSKelly Yancey 	struct pagename *nameentry;
115839867613SJohan Karlsson 	const char *pagedb_path;
1159e341cfd2SAlexander Motin 	int len, off, page, subpage;
11607e32b20dSKelly Yancey 
11617e32b20dSKelly Yancey 	if ((pagedb_path = getenv("SCSI_MODES")) == NULL)
11627e32b20dSKelly Yancey 		pagedb_path = DEFAULT_SCSI_MODE_DB;
11637e32b20dSKelly Yancey 
116454644e21SAlexander Motin 	if (load_format(pagedb_path, 0, 0) != 0 && verbose && errno == ENOENT) {
11657e32b20dSKelly Yancey 		/* Modepage database file not found. */
11667e32b20dSKelly Yancey 		warn("cannot open modepage database \"%s\"", pagedb_path);
11677e32b20dSKelly Yancey 	}
11687e32b20dSKelly Yancey 
11697e32b20dSKelly Yancey 	/* Build the list of all mode pages by querying the "all pages" page. */
117009128776SAlexander Motin 	mode_sense(device, &cdb_len, dbd, 0, pc, SMS_ALL_PAGES_PAGE,
117154644e21SAlexander Motin 	    subpages ? SMS_SUBPAGE_ALL : 0,
1172492a2ef5SKenneth D. Merry 	    task_attr, retry_count, timeout, data, sizeof(data));
11737e32b20dSKelly Yancey 
1174e341cfd2SAlexander Motin 	/* Skip block descriptors. */
1175e341cfd2SAlexander Motin 	if (cdb_len == 6) {
1176e341cfd2SAlexander Motin 		struct scsi_mode_header_6 *mh =
1177e341cfd2SAlexander Motin 		    (struct scsi_mode_header_6 *)data;
1178e341cfd2SAlexander Motin 		len = mh->data_length;
1179e341cfd2SAlexander Motin 		off = sizeof(*mh) + mh->blk_desc_len;
1180e341cfd2SAlexander Motin 	} else {
1181e341cfd2SAlexander Motin 		struct scsi_mode_header_10 *mh =
1182e341cfd2SAlexander Motin 		    (struct scsi_mode_header_10 *)data;
1183e341cfd2SAlexander Motin 		len = scsi_2btoul(mh->data_length);
1184e341cfd2SAlexander Motin 		off = sizeof(*mh) + scsi_2btoul(mh->blk_desc_len);
1185e341cfd2SAlexander Motin 	}
11867e32b20dSKelly Yancey 	/* Iterate through the pages in the reply. */
1187e341cfd2SAlexander Motin 	while (off < len) {
11887e32b20dSKelly Yancey 		/* Locate the next mode page header. */
1189e341cfd2SAlexander Motin 		mph = (struct scsi_mode_page_header *)(data + off);
11907e32b20dSKelly Yancey 
119154644e21SAlexander Motin 		if ((mph->page_code & SMPH_SPF) == 0) {
119254644e21SAlexander Motin 			page = mph->page_code & SMS_PAGE_CODE;
119354644e21SAlexander Motin 			subpage = 0;
1194e341cfd2SAlexander Motin 			off += sizeof(*mph) + mph->page_length;
119554644e21SAlexander Motin 		} else {
119654644e21SAlexander Motin 			mphsp = (struct scsi_mode_page_header_sp *)mph;
119754644e21SAlexander Motin 			page = mphsp->page_code & SMS_PAGE_CODE;
119854644e21SAlexander Motin 			subpage = mphsp->subpage;
1199e341cfd2SAlexander Motin 			off += sizeof(*mphsp) + scsi_2btoul(mphsp->page_length);
120054644e21SAlexander Motin 		}
12017e32b20dSKelly Yancey 
120254644e21SAlexander Motin 		nameentry = nameentry_lookup(page, subpage);
120354644e21SAlexander Motin 		if (subpage == 0) {
120454644e21SAlexander Motin 			printf("0x%02x\t%s\n", page,
120554644e21SAlexander Motin 			    nameentry ? nameentry->name : "");
120654644e21SAlexander Motin 		} else {
120754644e21SAlexander Motin 			printf("0x%02x,0x%02x\t%s\n", page, subpage,
120854644e21SAlexander Motin 			    nameentry ? nameentry->name : "");
120954644e21SAlexander Motin 		}
1210525689f1SJustin T. Gibbs 	}
1211525689f1SJustin T. Gibbs }
1212