2 * Copyright (c) 2000 Kelly Yancey <kbyanc@posi.net>
3 * Derived from work done by Julian Elischer <julian@tfs.com,
4 * julian@dialix.oz.au>, 1993, and Peter Dufault <dufault@hda.com>, 1994.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer,
12 * without modification, immediately at the beginning of the file.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 * $FreeBSD: src/sbin/camcontrol/modeedit.c,v 1.5.2.2 2003/01/08 17:55:02 njl Exp $
31 #include <sys/queue.h>
32 #include <sys/types.h>
44 #include <cam/scsi/scsi_all.h>
46 #include <cam/cam_ccb.h>
48 #include "camcontrol.h"
52 #define DEFAULT_SCSI_MODE_DB "/usr/share/misc/scsi_modes"
53 #define DEFAULT_EDITOR "vi"
54 #define MAX_FORMAT_SPEC 4096 /* Max CDB format specifier. */
55 #define MAX_PAGENUM_LEN 10 /* Max characters in page num. */
56 #define MAX_PAGENAME_LEN 64 /* Max characters in page name. */
57 #define PAGEDEF_START '{' /* Page definition delimiter. */
58 #define PAGEDEF_END '}' /* Page definition delimiter. */
59 #define PAGENAME_START '"' /* Page name delimiter. */
60 #define PAGENAME_END '"' /* Page name delimiter. */
61 #define PAGEENTRY_END ';' /* Page entry terminator (optional). */
62 #define MAX_COMMAND_SIZE 255 /* Mode/Log sense data buffer size. */
63 #define PAGE_CTRL_SHIFT 6 /* Bit offset to page control field. */
66 /* Macros for working with mode pages. */
67 #define MODE_PAGE_HEADER(mh) \
68 (struct scsi_mode_page_header *)find_mode_page_6(mh)
70 #define MODE_PAGE_DATA(mph) \
71 (u_int8_t *)(mph) + sizeof(struct scsi_mode_page_header)
75 STAILQ_ENTRY(editentry
) link
;
85 STAILQ_HEAD(, editentry
) editlist
; /* List of page entries. */
86 int editlist_changed
= 0; /* Whether any entries were changed. */
89 SLIST_ENTRY(pagename
) link
;
93 SLIST_HEAD(, pagename
) namelist
; /* Page number to name mappings. */
95 static char format
[MAX_FORMAT_SPEC
]; /* Buffer for scsi cdb format def. */
97 static FILE *edit_file
= NULL
; /* File handle for edit file. */
98 static char edit_path
[] = "/tmp/camXXXXXX";
101 /* Function prototypes. */
102 static void editentry_create(void *, int, void *, int, char *);
103 static void editentry_update(void *, int, void *, int, char *);
104 static int editentry_save(void *, char *);
105 static struct editentry
*editentry_lookup(char *);
106 static int editentry_set(char *, char *, int);
107 static void editlist_populate(struct cam_device
*, int, int, int,
109 static void editlist_save(struct cam_device
*, int, int, int, int,
111 static void nameentry_create(int, char *);
112 static struct pagename
*nameentry_lookup(int);
113 static int load_format(const char *, int);
114 static int modepage_write(FILE *, int);
115 static int modepage_read(FILE *);
116 static void modepage_edit(void);
117 static void modepage_dump(struct cam_device
*, int, int, int, int,
119 static void cleanup_editfile(void);
122 #define returnerr(code) do { \
128 #define RTRIM(string) do { \
130 while (isspace(string[_length = strlen(string) - 1])) \
131 string[_length] = '\0'; \
136 editentry_create(void *hook __unused
, int letter
, void *arg
, int count
,
139 struct editentry
*newentry
; /* Buffer to hold new entry. */
141 /* Allocate memory for the new entry and a copy of the entry name. */
142 if ((newentry
= malloc(sizeof(struct editentry
))) == NULL
||
143 (newentry
->name
= strdup(name
)) == NULL
)
146 /* Trim any trailing whitespace for the entry name. */
147 RTRIM(newentry
->name
);
149 newentry
->editable
= (arg
!= NULL
);
150 newentry
->type
= letter
;
151 newentry
->size
= count
; /* Placeholder; not accurate. */
152 newentry
->value
.svalue
= NULL
;
154 STAILQ_INSERT_TAIL(&editlist
, newentry
, link
);
158 editentry_update(void *hook __unused
, int letter
, void *arg
, int count
,
161 struct editentry
*dest
; /* Buffer to hold entry to update. */
163 dest
= editentry_lookup(name
);
164 assert(dest
!= NULL
);
167 dest
->size
= count
; /* We get the real size now. */
169 switch (dest
->type
) {
170 case 'i': /* Byte-sized integral type. */
171 case 'b': /* Bit-sized integral types. */
173 dest
->value
.ivalue
= (intptr_t)arg
;
176 case 'c': /* Character array. */
177 case 'z': /* Null-padded string. */
178 editentry_set(name
, (char *)arg
, 0);
186 editentry_save(void *hook __unused
, char *name
)
188 struct editentry
*src
; /* Entry value to save. */
190 src
= editentry_lookup(name
);
194 case 'i': /* Byte-sized integral type. */
195 case 'b': /* Bit-sized integral types. */
197 return (src
->value
.ivalue
);
200 case 'c': /* Character array. */
201 case 'z': /* Null-padded string. */
202 return ((intptr_t)src
->value
.svalue
);
209 return (0); /* This should never happen. */
212 static struct editentry
*
213 editentry_lookup(char *name
)
215 struct editentry
*scan
;
217 assert(name
!= NULL
);
219 STAILQ_FOREACH(scan
, &editlist
, link
) {
220 if (strcasecmp(scan
->name
, name
) == 0)
224 /* Not found during list traversal. */
229 editentry_set(char *name
, char *newvalue
, int editonly
)
231 struct editentry
*dest
; /* Modepage entry to update. */
232 char *cval
; /* Pointer to new string value. */
233 char *convertend
; /* End-of-conversion pointer. */
234 int ival
; /* New integral value. */
235 int resolution
; /* Resolution in bits for integer conversion. */
236 int resolution_max
; /* Maximum resolution for modepage's size. */
238 assert(newvalue
!= NULL
);
239 if (*newvalue
== '\0')
240 return (0); /* Nothing to do. */
242 if ((dest
= editentry_lookup(name
)) == NULL
)
244 if (!dest
->editable
&& editonly
)
247 switch (dest
->type
) {
248 case 'i': /* Byte-sized integral type. */
249 case 'b': /* Bit-sized integral types. */
251 /* Convert the value string to an integer. */
252 resolution
= (dest
->type
== 'i')? 8: 1;
253 ival
= (int)strtol(newvalue
, &convertend
, 0);
254 if (*convertend
!= '\0')
258 * Determine the maximum value of the given size for the
259 * current resolution.
260 * XXX Lovely x86's optimize out the case of shifting by 32,
261 * and gcc doesn't currently workaround it (even for int64's),
262 * so we have to kludge it.
264 if (resolution
* dest
->size
== 32)
265 resolution_max
= 0xffffffff;
267 resolution_max
= (1 << (resolution
* dest
->size
)) - 1;
269 if (ival
> resolution_max
|| ival
< 0) {
270 int newival
= (ival
< 0) ? 0 : resolution_max
;
271 warnx("value %d is out of range for entry %s; clipping "
272 "to %d", ival
, name
, newival
);
275 if (dest
->value
.ivalue
!= ival
)
276 editlist_changed
= 1;
277 dest
->value
.ivalue
= ival
;
280 case 'c': /* Character array. */
281 case 'z': /* Null-padded string. */
282 if ((cval
= malloc(dest
->size
+ 1)) == NULL
)
284 bzero(cval
, dest
->size
+ 1);
285 strncpy(cval
, newvalue
, dest
->size
);
286 if (dest
->type
== 'z') {
287 /* Convert trailing spaces to nulls. */
290 for (conv_end
= cval
+ dest
->size
;
291 conv_end
>= cval
; conv_end
--) {
292 if (*conv_end
== ' ')
294 else if (*conv_end
!= '\0')
298 if (strncmp(dest
->value
.svalue
, cval
, dest
->size
) == 0) {
299 /* Nothing changed, free the newly allocated string. */
303 if (dest
->value
.svalue
!= NULL
) {
304 /* Free the current string buffer. */
305 free(dest
->value
.svalue
);
306 dest
->value
.svalue
= NULL
;
308 dest
->value
.svalue
= cval
;
309 editlist_changed
= 1;
320 nameentry_create(int pagenum
, char *name
) {
321 struct pagename
*newentry
;
323 if (pagenum
< 0 || name
== NULL
|| name
[0] == '\0')
326 /* Allocate memory for the new entry and a copy of the entry name. */
327 if ((newentry
= malloc(sizeof(struct pagename
))) == NULL
||
328 (newentry
->name
= strdup(name
)) == NULL
)
331 /* Trim any trailing whitespace for the page name. */
332 RTRIM(newentry
->name
);
334 newentry
->pagenum
= pagenum
;
335 SLIST_INSERT_HEAD(&namelist
, newentry
, link
);
338 static struct pagename
*
339 nameentry_lookup(int pagenum
) {
340 struct pagename
*scan
;
342 SLIST_FOREACH(scan
, &namelist
, link
) {
343 if (pagenum
== scan
->pagenum
)
347 /* Not found during list traversal. */
352 load_format(const char *pagedb_path
, int page
)
355 char str_pagenum
[MAX_PAGENUM_LEN
];
356 char str_pagename
[MAX_PAGENAME_LEN
];
358 int depth
; /* Quoting depth. */
361 enum { LOCATE
, PAGENAME
, PAGEDEF
} state
;
364 #define SETSTATE_LOCATE do { \
365 str_pagenum[0] = '\0'; \
366 str_pagename[0] = '\0'; \
371 #define SETSTATE_PAGENAME do { \
372 str_pagename[0] = '\0'; \
376 #define SETSTATE_PAGEDEF do { \
381 #define UPDATE_LINENO do { \
386 #define BUFFERFULL(buffer) (strlen(buffer) + 1 >= sizeof(buffer))
388 if ((pagedb
= fopen(pagedb_path
, "r")) == NULL
)
391 SLIST_INIT(&namelist
);
397 while ((c
= fgetc(pagedb
)) != EOF
) {
399 /* Keep a line count to make error messages more useful. */
402 /* Skip over comments anywhere in the mode database. */
406 } while (c
!= '\n' && c
!= EOF
);
411 /* Strip out newline characters. */
415 /* Keep track of the nesting depth for braces. */
416 if (c
== PAGEDEF_START
)
418 else if (c
== PAGEDEF_END
) {
421 errx(EX_OSFILE
, "%s:%d: %s", pagedb_path
,
422 lineno
, "mismatched bracket");
429 * Locate the page the user is interested in, skipping
433 /* Ignore all whitespace between pages. */
435 } else if (depth
== 0 && c
== PAGEENTRY_END
) {
437 * A page entry terminator will reset page
438 * scanning (useful for assigning names to
439 * modes without providing a mode definition).
441 /* Record the name of this page. */
442 pagenum
= strtol(str_pagenum
, NULL
, 0);
443 nameentry_create(pagenum
, str_pagename
);
445 } else if (depth
== 0 && c
== PAGENAME_START
) {
447 } else if (c
== PAGEDEF_START
) {
448 pagenum
= strtol(str_pagenum
, NULL
, 0);
450 /* Record the name of this page. */
451 nameentry_create(pagenum
, str_pagename
);
453 * Only record the format if this is
454 * the page we are interested in.
456 if (page
== pagenum
&& !found
)
459 } else if (c
== PAGEDEF_END
) {
460 /* Reset the processor state. */
462 } else if (depth
== 0 && ! BUFFERFULL(str_pagenum
)) {
463 strncat(str_pagenum
, &c
, 1);
464 } else if (depth
== 0) {
465 errx(EX_OSFILE
, "%s:%d: %s %zd %s", pagedb_path
,
466 lineno
, "page identifier exceeds",
467 sizeof(str_pagenum
) - 1, "characters");
472 if (c
== PAGENAME_END
) {
474 * Return to LOCATE state without resetting the
475 * page number buffer.
478 } else if (! BUFFERFULL(str_pagename
)) {
479 strncat(str_pagename
, &c
, 1);
481 errx(EX_OSFILE
, "%s:%d: %s %zd %s", pagedb_path
,
482 lineno
, "page name exceeds",
483 sizeof(str_pagenum
) - 1, "characters");
489 * Transfer the page definition into a format buffer
490 * suitable for use with CDB encoding/decoding routines.
495 } else if (! BUFFERFULL(format
)) {
496 strncat(format
, &c
, 1);
498 errx(EX_OSFILE
, "%s:%d: %s %zd %s", pagedb_path
,
499 lineno
, "page definition exceeds",
500 sizeof(format
) - 1, "characters");
508 /* Repeat processing loop with next character. */
512 err(EX_OSFILE
, "%s", pagedb_path
);
514 /* Close the SCSI page database. */
517 if (!found
) /* Never found a matching page. */
524 editlist_populate(struct cam_device
*device
, int modepage
, int page_control
,
525 int dbd
, int retries
, int timeout
)
527 u_int8_t data
[MAX_COMMAND_SIZE
];/* Buffer to hold sense data. */
528 u_int8_t
*mode_pars
; /* Pointer to modepage params. */
529 struct scsi_mode_header_6
*mh
; /* Location of mode header. */
530 struct scsi_mode_page_header
*mph
;
532 STAILQ_INIT(&editlist
);
534 /* Fetch changeable values; use to build initial editlist. */
535 mode_sense(device
, modepage
, 1, dbd
, retries
, timeout
, data
,
538 mh
= (struct scsi_mode_header_6
*)data
;
539 mph
= MODE_PAGE_HEADER(mh
);
540 mode_pars
= MODE_PAGE_DATA(mph
);
542 /* Decode the value data, creating edit_entries for each value. */
543 buff_decode_visit(mode_pars
, mh
->data_length
, format
,
544 editentry_create
, 0);
546 /* Fetch the current/saved values; use to set editentry values. */
547 mode_sense(device
, modepage
, page_control
, dbd
, retries
, timeout
, data
,
549 buff_decode_visit(mode_pars
, mh
->data_length
, format
,
550 editentry_update
, 0);
554 editlist_save(struct cam_device
*device
, int modepage
, int page_control
,
555 int dbd
, int retries
, int timeout
)
557 u_int8_t data
[MAX_COMMAND_SIZE
];/* Buffer to hold sense data. */
558 u_int8_t
*mode_pars
; /* Pointer to modepage params. */
559 struct scsi_mode_header_6
*mh
; /* Location of mode header. */
560 struct scsi_mode_page_header
*mph
;
562 /* Make sure that something changed before continuing. */
563 if (! editlist_changed
)
567 * Preload the CDB buffer with the current mode page data.
568 * XXX If buff_encode_visit would return the number of bytes encoded
569 * we *should* use that to build a header from scratch. As it is
570 * now, we need mode_sense to find out the page length.
572 mode_sense(device
, modepage
, page_control
, dbd
, retries
, timeout
, data
,
575 /* Initial headers & offsets. */
576 mh
= (struct scsi_mode_header_6
*)data
;
577 mph
= MODE_PAGE_HEADER(mh
);
578 mode_pars
= MODE_PAGE_DATA(mph
);
580 /* Encode the value data to be passed back to the device. */
581 buff_encode_visit(mode_pars
, mh
->data_length
, format
,
584 /* Eliminate block descriptors. */
585 bcopy(mph
, ((u_int8_t
*)mh
) + sizeof(*mh
),
586 sizeof(*mph
) + mph
->page_length
);
588 /* Recalculate headers & offsets. */
589 mh
->blk_desc_len
= 0; /* No block descriptors. */
590 mh
->dev_spec
= 0; /* Clear device-specific parameters. */
591 mph
= MODE_PAGE_HEADER(mh
);
592 mode_pars
= MODE_PAGE_DATA(mph
);
594 mph
->page_code
&= SMS_PAGE_CODE
;/* Isolate just the page code. */
595 mh
->data_length
= 0; /* Reserved for MODE SELECT command. */
598 * Write the changes back to the device. If the user editted control
599 * page 3 (saved values) then request the changes be permanently
603 (page_control
<< PAGE_CTRL_SHIFT
== SMS_PAGE_CTRL_SAVED
),
604 retries
, timeout
, (u_int8_t
*)mh
,
605 sizeof(*mh
) + mh
->blk_desc_len
+ sizeof(*mph
) + mph
->page_length
);
609 modepage_write(FILE *file
, int editonly
)
611 struct editentry
*scan
;
614 STAILQ_FOREACH(scan
, &editlist
, link
) {
615 if (scan
->editable
|| !editonly
) {
617 if (scan
->type
== 'c' || scan
->type
== 'z') {
618 fprintf(file
, "%s: %s\n", scan
->name
,
621 fprintf(file
, "%s: %d\n", scan
->name
,
630 modepage_read(FILE *file
)
632 char *buffer
; /* Pointer to dynamic line buffer. */
633 char *line
; /* Pointer to static fgetln buffer. */
634 char *name
; /* Name portion of the line buffer. */
635 char *value
; /* Value portion of line buffer. */
636 size_t length
; /* Length of static fgetln buffer. */
638 #define ABORT_READ(message, param) do { \
639 warnx(message, param); \
644 while ((line
= fgetln(file
, &length
)) != NULL
) {
645 /* Trim trailing whitespace (including optional newline). */
646 while (length
> 0 && isspace(line
[length
- 1]))
649 /* Allocate a buffer to hold the line + terminating null. */
650 if ((buffer
= malloc(length
+ 1)) == NULL
)
652 memcpy(buffer
, line
, length
);
653 buffer
[length
] = '\0';
655 /* Strip out comments. */
656 if ((value
= strchr(buffer
, '#')) != NULL
)
659 /* The name is first in the buffer. Trim whitespace.*/
662 while (isspace(*name
))
665 /* Skip empty lines. */
666 if (strlen(name
) == 0)
669 /* The name ends at the colon; the value starts there. */
670 if ((value
= strrchr(buffer
, ':')) == NULL
)
671 ABORT_READ("no value associated with %s", name
);
672 *value
= '\0'; /* Null-terminate name. */
673 value
++; /* Value starts afterwards. */
675 /* Trim leading and trailing whitespace. */
677 while (isspace(*value
))
680 /* Make sure there is a value left. */
681 if (strlen(value
) == 0)
682 ABORT_READ("no value associated with %s", name
);
684 /* Update our in-memory copy of the modepage entry value. */
685 if (editentry_set(name
, value
, 1) != 0) {
686 if (errno
== ENOENT
) {
687 /* No entry by the name. */
688 ABORT_READ("no such modepage entry \"%s\"",
690 } else if (errno
== EINVAL
) {
692 ABORT_READ("Invalid value for entry \"%s\"",
694 } else if (errno
== ERANGE
) {
695 /* Value out of range for entry type. */
696 ABORT_READ("value out of range for %s", name
);
697 } else if (errno
== EPERM
) {
698 /* Entry is not editable; not fatal. */
699 warnx("modepage entry \"%s\" is read-only; "
706 return (ferror(file
)? -1: 0);
719 if (!isatty(fileno(stdin
))) {
720 /* Not a tty, read changes from stdin. */
721 modepage_read(stdin
);
725 /* Lookup editor to invoke. */
726 if ((editor
= getenv("EDITOR")) == NULL
)
727 editor
= DEFAULT_EDITOR
;
729 /* Create temp file for editor to modify. */
730 if ((fd
= mkstemp(edit_path
)) == -1)
731 errx(EX_CANTCREAT
, "mkstemp failed");
733 atexit(cleanup_editfile
);
735 if ((edit_file
= fdopen(fd
, "w")) == NULL
)
736 err(EX_NOINPUT
, "%s", edit_path
);
738 written
= modepage_write(edit_file
, 1);
744 warnx("no editable entries");
750 * Allocate memory to hold the command line (the 2 extra characters
751 * are to hold the argument separator (a space), and the terminating
754 commandline
= malloc(strlen(editor
) + strlen(edit_path
) + 2);
755 if (commandline
== NULL
)
757 sprintf(commandline
, "%s %s", editor
, edit_path
);
759 /* Invoke the editor on the temp file. */
760 if (system(commandline
) == -1)
761 err(EX_UNAVAILABLE
, "could not invoke %s", editor
);
764 if ((edit_file
= fopen(edit_path
, "r")) == NULL
)
765 err(EX_NOINPUT
, "%s", edit_path
);
767 /* Read any changes made to the temp file. */
768 modepage_read(edit_file
);
774 modepage_dump(struct cam_device
*device
, int page
, int page_control
, int dbd
,
775 int retries
, int timeout
)
777 u_int8_t data
[MAX_COMMAND_SIZE
];/* Buffer to hold sense data. */
778 u_int8_t
*mode_pars
; /* Pointer to modepage params. */
779 struct scsi_mode_header_6
*mh
; /* Location of mode header. */
780 struct scsi_mode_page_header
*mph
;
781 int mode_idx
; /* Index for scanning mode params. */
783 mode_sense(device
, page
, page_control
, dbd
, retries
, timeout
, data
,
786 mh
= (struct scsi_mode_header_6
*)data
;
787 mph
= MODE_PAGE_HEADER(mh
);
788 mode_pars
= MODE_PAGE_DATA(mph
);
790 /* Print the raw mode page data with newlines each 8 bytes. */
791 for (mode_idx
= 0; mode_idx
< mph
->page_length
; mode_idx
++) {
792 printf("%02x%c", mode_pars
[mode_idx
],
793 (((mode_idx
+ 1) % 8) == 0) ? '\n' : ' ');
799 cleanup_editfile(void)
801 if (edit_file
== NULL
)
803 if (fclose(edit_file
) != 0 || unlink(edit_path
) != 0)
804 warn("%s", edit_path
);
809 mode_edit(struct cam_device
*device
, int page
, int page_control
, int dbd
,
810 int edit
, int binary
, int retry_count
, int timeout
)
812 const char *pagedb_path
; /* Path to modepage database. */
815 errx(EX_USAGE
, "cannot edit in binary mode.");
818 if ((pagedb_path
= getenv("SCSI_MODES")) == NULL
)
819 pagedb_path
= DEFAULT_SCSI_MODE_DB
;
821 if (load_format(pagedb_path
, page
) != 0 && (edit
|| verbose
)) {
822 if (errno
== ENOENT
) {
823 /* Modepage database file not found. */
824 warn("cannot open modepage database \"%s\"",
826 } else if (errno
== ESRCH
) {
827 /* Modepage entry not found in database. */
828 warnx("modepage %d not found in database"
829 "\"%s\"", page
, pagedb_path
);
831 /* We can recover in display mode, otherwise we exit. */
833 warnx("reverting to binary display only");
839 editlist_populate(device
, page
, page_control
, dbd
, retry_count
,
844 if (page_control
<< PAGE_CTRL_SHIFT
!= SMS_PAGE_CTRL_CURRENT
&&
845 page_control
<< PAGE_CTRL_SHIFT
!= SMS_PAGE_CTRL_SAVED
)
846 errx(EX_USAGE
, "it only makes sense to edit page 0 "
847 "(current) or page 3 (saved values)");
849 editlist_save(device
, page
, page_control
, dbd
, retry_count
,
851 } else if (binary
|| STAILQ_EMPTY(&editlist
)) {
852 /* Display without formatting information. */
853 modepage_dump(device
, page
, page_control
, dbd
, retry_count
,
856 /* Display with format. */
857 modepage_write(stdout
, 0);
862 mode_list(struct cam_device
*device
, int page_control
, int dbd
,
863 int retry_count
, int timeout
)
865 u_int8_t data
[MAX_COMMAND_SIZE
];/* Buffer to hold sense data. */
866 struct scsi_mode_header_6
*mh
; /* Location of mode header. */
867 struct scsi_mode_page_header
*mph
;
868 struct pagename
*nameentry
;
869 const char *pagedb_path
;
872 if ((pagedb_path
= getenv("SCSI_MODES")) == NULL
)
873 pagedb_path
= DEFAULT_SCSI_MODE_DB
;
875 if (load_format(pagedb_path
, 0) != 0 && verbose
&& errno
== ENOENT
) {
876 /* Modepage database file not found. */
877 warn("cannot open modepage database \"%s\"", pagedb_path
);
880 /* Build the list of all mode pages by querying the "all pages" page. */
881 mode_sense(device
, SMS_ALL_PAGES_PAGE
, page_control
, dbd
, retry_count
,
882 timeout
, data
, sizeof(data
));
884 mh
= (struct scsi_mode_header_6
*)data
;
885 len
= mh
->blk_desc_len
; /* Skip block descriptors. */
886 /* Iterate through the pages in the reply. */
887 while (len
< mh
->data_length
) {
888 /* Locate the next mode page header. */
889 mph
= (struct scsi_mode_page_header
*)
890 ((intptr_t)mh
+ sizeof(*mh
) + len
);
892 mph
->page_code
&= SMS_PAGE_CODE
;
893 nameentry
= nameentry_lookup(mph
->page_code
);
895 if (nameentry
== NULL
|| nameentry
->name
== NULL
)
896 printf("0x%02x\n", mph
->page_code
);
898 printf("0x%02x\t%s\n", mph
->page_code
,
900 len
+= mph
->page_length
+ sizeof(*mph
);