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
;
365 #define SETSTATE_LOCATE do { \
366 str_pagenum[0] = '\0'; \
367 str_pagename[0] = '\0'; \
372 #define SETSTATE_PAGENAME do { \
373 str_pagename[0] = '\0'; \
377 #define SETSTATE_PAGEDEF do { \
382 #define UPDATE_LINENO do { \
387 #define BUFFERFULL(buffer) (strlen(buffer) + 1 >= sizeof(buffer))
389 if ((pagedb
= fopen(pagedb_path
, "r")) == NULL
)
392 SLIST_INIT(&namelist
);
398 while ((cc
= fgetc(pagedb
)) != EOF
) {
401 /* Keep a line count to make error messages more useful. */
404 /* Skip over comments anywhere in the mode database. */
408 } while (cc
!= '\n' && cc
!= EOF
);
414 /* Strip out newline characters. */
418 /* Keep track of the nesting depth for braces. */
419 if (c
== PAGEDEF_START
)
421 else if (c
== PAGEDEF_END
) {
424 errx(EX_OSFILE
, "%s:%d: %s", pagedb_path
,
425 lineno
, "mismatched bracket");
432 * Locate the page the user is interested in, skipping
436 /* Ignore all whitespace between pages. */
438 } else if (depth
== 0 && c
== PAGEENTRY_END
) {
440 * A page entry terminator will reset page
441 * scanning (useful for assigning names to
442 * modes without providing a mode definition).
444 /* Record the name of this page. */
445 pagenum
= strtol(str_pagenum
, NULL
, 0);
446 nameentry_create(pagenum
, str_pagename
);
448 } else if (depth
== 0 && c
== PAGENAME_START
) {
450 } else if (c
== PAGEDEF_START
) {
451 pagenum
= strtol(str_pagenum
, NULL
, 0);
453 /* Record the name of this page. */
454 nameentry_create(pagenum
, str_pagename
);
456 * Only record the format if this is
457 * the page we are interested in.
459 if (page
== pagenum
&& !found
)
462 } else if (c
== PAGEDEF_END
) {
463 /* Reset the processor state. */
465 } else if (depth
== 0 && ! BUFFERFULL(str_pagenum
)) {
466 strncat(str_pagenum
, &c
, 1);
467 } else if (depth
== 0) {
468 errx(EX_OSFILE
, "%s:%d: %s %zd %s", pagedb_path
,
469 lineno
, "page identifier exceeds",
470 sizeof(str_pagenum
) - 1, "characters");
475 if (c
== PAGENAME_END
) {
477 * Return to LOCATE state without resetting the
478 * page number buffer.
481 } else if (! BUFFERFULL(str_pagename
)) {
482 strncat(str_pagename
, &c
, 1);
484 errx(EX_OSFILE
, "%s:%d: %s %zd %s", pagedb_path
,
485 lineno
, "page name exceeds",
486 sizeof(str_pagenum
) - 1, "characters");
492 * Transfer the page definition into a format buffer
493 * suitable for use with CDB encoding/decoding routines.
498 } else if (! BUFFERFULL(format
)) {
499 strncat(format
, &c
, 1);
501 errx(EX_OSFILE
, "%s:%d: %s %zd %s", pagedb_path
,
502 lineno
, "page definition exceeds",
503 sizeof(format
) - 1, "characters");
511 /* Repeat processing loop with next character. */
515 err(EX_OSFILE
, "%s", pagedb_path
);
517 /* Close the SCSI page database. */
520 if (!found
) /* Never found a matching page. */
527 editlist_populate(struct cam_device
*device
, int modepage
, int page_control
,
528 int dbd
, int retries
, int timeout
)
530 u_int8_t data
[MAX_COMMAND_SIZE
];/* Buffer to hold sense data. */
531 u_int8_t
*mode_pars
; /* Pointer to modepage params. */
532 struct scsi_mode_header_6
*mh
; /* Location of mode header. */
533 struct scsi_mode_page_header
*mph
;
535 STAILQ_INIT(&editlist
);
537 /* Fetch changeable values; use to build initial editlist. */
538 mode_sense(device
, modepage
, 1, dbd
, retries
, timeout
, data
,
541 mh
= (struct scsi_mode_header_6
*)data
;
542 mph
= MODE_PAGE_HEADER(mh
);
543 mode_pars
= MODE_PAGE_DATA(mph
);
545 /* Decode the value data, creating edit_entries for each value. */
546 buff_decode_visit(mode_pars
, mh
->data_length
, format
,
547 editentry_create
, 0);
549 /* Fetch the current/saved values; use to set editentry values. */
550 mode_sense(device
, modepage
, page_control
, dbd
, retries
, timeout
, data
,
552 buff_decode_visit(mode_pars
, mh
->data_length
, format
,
553 editentry_update
, 0);
557 editlist_save(struct cam_device
*device
, int modepage
, int page_control
,
558 int dbd
, int retries
, int timeout
)
560 u_int8_t data
[MAX_COMMAND_SIZE
];/* Buffer to hold sense data. */
561 u_int8_t
*mode_pars
; /* Pointer to modepage params. */
562 struct scsi_mode_header_6
*mh
; /* Location of mode header. */
563 struct scsi_mode_page_header
*mph
;
565 /* Make sure that something changed before continuing. */
566 if (! editlist_changed
)
570 * Preload the CDB buffer with the current mode page data.
571 * XXX If buff_encode_visit would return the number of bytes encoded
572 * we *should* use that to build a header from scratch. As it is
573 * now, we need mode_sense to find out the page length.
575 mode_sense(device
, modepage
, page_control
, dbd
, retries
, timeout
, data
,
578 /* Initial headers & offsets. */
579 mh
= (struct scsi_mode_header_6
*)data
;
580 mph
= MODE_PAGE_HEADER(mh
);
581 mode_pars
= MODE_PAGE_DATA(mph
);
583 /* Encode the value data to be passed back to the device. */
584 buff_encode_visit(mode_pars
, mh
->data_length
, format
,
587 /* Eliminate block descriptors. */
588 bcopy(mph
, ((u_int8_t
*)mh
) + sizeof(*mh
),
589 sizeof(*mph
) + mph
->page_length
);
591 /* Recalculate headers & offsets. */
592 mh
->blk_desc_len
= 0; /* No block descriptors. */
593 mh
->dev_spec
= 0; /* Clear device-specific parameters. */
594 mph
= MODE_PAGE_HEADER(mh
);
595 mode_pars
= MODE_PAGE_DATA(mph
);
597 mph
->page_code
&= SMS_PAGE_CODE
;/* Isolate just the page code. */
598 mh
->data_length
= 0; /* Reserved for MODE SELECT command. */
601 * Write the changes back to the device. If the user editted control
602 * page 3 (saved values) then request the changes be permanently
606 (page_control
<< PAGE_CTRL_SHIFT
== SMS_PAGE_CTRL_SAVED
),
607 retries
, timeout
, (u_int8_t
*)mh
,
608 sizeof(*mh
) + mh
->blk_desc_len
+ sizeof(*mph
) + mph
->page_length
);
612 modepage_write(FILE *file
, int editonly
)
614 struct editentry
*scan
;
617 STAILQ_FOREACH(scan
, &editlist
, link
) {
618 if (scan
->editable
|| !editonly
) {
620 if (scan
->type
== 'c' || scan
->type
== 'z') {
621 fprintf(file
, "%s: %s\n", scan
->name
,
624 fprintf(file
, "%s: %d\n", scan
->name
,
633 modepage_read(FILE *file
)
635 char *buffer
; /* Pointer to dynamic line buffer. */
636 char *line
; /* Pointer to static fgetln buffer. */
637 char *name
; /* Name portion of the line buffer. */
638 char *value
; /* Value portion of line buffer. */
639 size_t length
; /* Length of static fgetln buffer. */
641 #define ABORT_READ(message, param) do { \
642 warnx(message, param); \
647 while ((line
= fgetln(file
, &length
)) != NULL
) {
648 /* Trim trailing whitespace (including optional newline). */
649 while (length
> 0 && isspace(line
[length
- 1]))
652 /* Allocate a buffer to hold the line + terminating null. */
653 if ((buffer
= malloc(length
+ 1)) == NULL
)
655 memcpy(buffer
, line
, length
);
656 buffer
[length
] = '\0';
658 /* Strip out comments. */
659 if ((value
= strchr(buffer
, '#')) != NULL
)
662 /* The name is first in the buffer. Trim whitespace.*/
665 while (isspace(*name
))
668 /* Skip empty lines. */
669 if (strlen(name
) == 0)
672 /* The name ends at the colon; the value starts there. */
673 if ((value
= strrchr(buffer
, ':')) == NULL
)
674 ABORT_READ("no value associated with %s", name
);
675 *value
= '\0'; /* Null-terminate name. */
676 value
++; /* Value starts afterwards. */
678 /* Trim leading and trailing whitespace. */
680 while (isspace(*value
))
683 /* Make sure there is a value left. */
684 if (strlen(value
) == 0)
685 ABORT_READ("no value associated with %s", name
);
687 /* Update our in-memory copy of the modepage entry value. */
688 if (editentry_set(name
, value
, 1) != 0) {
689 if (errno
== ENOENT
) {
690 /* No entry by the name. */
691 ABORT_READ("no such modepage entry \"%s\"",
693 } else if (errno
== EINVAL
) {
695 ABORT_READ("Invalid value for entry \"%s\"",
697 } else if (errno
== ERANGE
) {
698 /* Value out of range for entry type. */
699 ABORT_READ("value out of range for %s", name
);
700 } else if (errno
== EPERM
) {
701 /* Entry is not editable; not fatal. */
702 warnx("modepage entry \"%s\" is read-only; "
709 return (ferror(file
)? -1: 0);
722 if (!isatty(fileno(stdin
))) {
723 /* Not a tty, read changes from stdin. */
724 modepage_read(stdin
);
728 /* Lookup editor to invoke. */
729 if ((editor
= getenv("EDITOR")) == NULL
)
730 editor
= DEFAULT_EDITOR
;
732 /* Create temp file for editor to modify. */
733 if ((fd
= mkstemp(edit_path
)) == -1)
734 errx(EX_CANTCREAT
, "mkstemp failed");
736 atexit(cleanup_editfile
);
738 if ((edit_file
= fdopen(fd
, "w")) == NULL
)
739 err(EX_NOINPUT
, "%s", edit_path
);
741 written
= modepage_write(edit_file
, 1);
747 warnx("no editable entries");
753 * Allocate memory to hold the command line (the 2 extra characters
754 * are to hold the argument separator (a space), and the terminating
757 commandline
= malloc(strlen(editor
) + strlen(edit_path
) + 2);
758 if (commandline
== NULL
)
760 sprintf(commandline
, "%s %s", editor
, edit_path
);
762 /* Invoke the editor on the temp file. */
763 if (system(commandline
) == -1)
764 err(EX_UNAVAILABLE
, "could not invoke %s", editor
);
767 if ((edit_file
= fopen(edit_path
, "r")) == NULL
)
768 err(EX_NOINPUT
, "%s", edit_path
);
770 /* Read any changes made to the temp file. */
771 modepage_read(edit_file
);
777 modepage_dump(struct cam_device
*device
, int page
, int page_control
, int dbd
,
778 int retries
, int timeout
)
780 u_int8_t data
[MAX_COMMAND_SIZE
];/* Buffer to hold sense data. */
781 u_int8_t
*mode_pars
; /* Pointer to modepage params. */
782 struct scsi_mode_header_6
*mh
; /* Location of mode header. */
783 struct scsi_mode_page_header
*mph
;
784 int mode_idx
; /* Index for scanning mode params. */
786 mode_sense(device
, page
, page_control
, dbd
, retries
, timeout
, data
,
789 mh
= (struct scsi_mode_header_6
*)data
;
790 mph
= MODE_PAGE_HEADER(mh
);
791 mode_pars
= MODE_PAGE_DATA(mph
);
793 /* Print the raw mode page data with newlines each 8 bytes. */
794 for (mode_idx
= 0; mode_idx
< mph
->page_length
; mode_idx
++) {
795 printf("%02x%c", mode_pars
[mode_idx
],
796 (((mode_idx
+ 1) % 8) == 0) ? '\n' : ' ');
802 cleanup_editfile(void)
804 if (edit_file
== NULL
)
806 if (fclose(edit_file
) != 0 || unlink(edit_path
) != 0)
807 warn("%s", edit_path
);
812 mode_edit(struct cam_device
*device
, int page
, int page_control
, int dbd
,
813 int edit
, int binary
, int retry_count
, int timeout
)
815 const char *pagedb_path
; /* Path to modepage database. */
818 errx(EX_USAGE
, "cannot edit in binary mode.");
821 if ((pagedb_path
= getenv("SCSI_MODES")) == NULL
)
822 pagedb_path
= DEFAULT_SCSI_MODE_DB
;
824 if (load_format(pagedb_path
, page
) != 0 && (edit
|| verbose
)) {
825 if (errno
== ENOENT
) {
826 /* Modepage database file not found. */
827 warn("cannot open modepage database \"%s\"",
829 } else if (errno
== ESRCH
) {
830 /* Modepage entry not found in database. */
831 warnx("modepage %d not found in database"
832 "\"%s\"", page
, pagedb_path
);
834 /* We can recover in display mode, otherwise we exit. */
836 warnx("reverting to binary display only");
842 editlist_populate(device
, page
, page_control
, dbd
, retry_count
,
847 if (page_control
<< PAGE_CTRL_SHIFT
!= SMS_PAGE_CTRL_CURRENT
&&
848 page_control
<< PAGE_CTRL_SHIFT
!= SMS_PAGE_CTRL_SAVED
)
849 errx(EX_USAGE
, "it only makes sense to edit page 0 "
850 "(current) or page 3 (saved values)");
852 editlist_save(device
, page
, page_control
, dbd
, retry_count
,
854 } else if (binary
|| STAILQ_EMPTY(&editlist
)) {
855 /* Display without formatting information. */
856 modepage_dump(device
, page
, page_control
, dbd
, retry_count
,
859 /* Display with format. */
860 modepage_write(stdout
, 0);
865 mode_list(struct cam_device
*device
, int page_control
, int dbd
,
866 int retry_count
, int timeout
)
868 u_int8_t data
[MAX_COMMAND_SIZE
];/* Buffer to hold sense data. */
869 struct scsi_mode_header_6
*mh
; /* Location of mode header. */
870 struct scsi_mode_page_header
*mph
;
871 struct pagename
*nameentry
;
872 const char *pagedb_path
;
875 if ((pagedb_path
= getenv("SCSI_MODES")) == NULL
)
876 pagedb_path
= DEFAULT_SCSI_MODE_DB
;
878 if (load_format(pagedb_path
, 0) != 0 && verbose
&& errno
== ENOENT
) {
879 /* Modepage database file not found. */
880 warn("cannot open modepage database \"%s\"", pagedb_path
);
883 /* Build the list of all mode pages by querying the "all pages" page. */
884 mode_sense(device
, SMS_ALL_PAGES_PAGE
, page_control
, dbd
, retry_count
,
885 timeout
, data
, sizeof(data
));
887 mh
= (struct scsi_mode_header_6
*)data
;
888 len
= mh
->blk_desc_len
; /* Skip block descriptors. */
889 /* Iterate through the pages in the reply. */
890 while (len
< mh
->data_length
) {
891 /* Locate the next mode page header. */
892 mph
= (struct scsi_mode_page_header
*)
893 ((intptr_t)mh
+ sizeof(*mh
) + len
);
895 mph
->page_code
&= SMS_PAGE_CODE
;
896 nameentry
= nameentry_lookup(mph
->page_code
);
898 if (nameentry
== NULL
|| nameentry
->name
== NULL
)
899 printf("0x%02x\n", mph
->page_code
);
901 printf("0x%02x\t%s\n", mph
->page_code
,
903 len
+= mph
->page_length
+ sizeof(*mph
);