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 $
29 * $DragonFly: src/sbin/camcontrol/modeedit.c,v 1.3 2005/01/11 23:58:55 cpressey Exp $
32 #include <sys/queue.h>
33 #include <sys/types.h>
45 #include <cam/scsi/scsi_all.h>
47 #include <cam/cam_ccb.h>
49 #include "camcontrol.h"
53 #define DEFAULT_SCSI_MODE_DB "/usr/share/misc/scsi_modes"
54 #define DEFAULT_EDITOR "vi"
55 #define MAX_FORMAT_SPEC 4096 /* Max CDB format specifier. */
56 #define MAX_PAGENUM_LEN 10 /* Max characters in page num. */
57 #define MAX_PAGENAME_LEN 64 /* Max characters in page name. */
58 #define PAGEDEF_START '{' /* Page definition delimiter. */
59 #define PAGEDEF_END '}' /* Page definition delimiter. */
60 #define PAGENAME_START '"' /* Page name delimiter. */
61 #define PAGENAME_END '"' /* Page name delimiter. */
62 #define PAGEENTRY_END ';' /* Page entry terminator (optional). */
63 #define MAX_COMMAND_SIZE 255 /* Mode/Log sense data buffer size. */
64 #define PAGE_CTRL_SHIFT 6 /* Bit offset to page control field. */
67 /* Macros for working with mode pages. */
68 #define MODE_PAGE_HEADER(mh) \
69 (struct scsi_mode_page_header *)find_mode_page_6(mh)
71 #define MODE_PAGE_DATA(mph) \
72 (u_int8_t *)(mph) + sizeof(struct scsi_mode_page_header)
76 STAILQ_ENTRY(editentry
) link
;
86 STAILQ_HEAD(, editentry
) editlist
; /* List of page entries. */
87 int editlist_changed
= 0; /* Whether any entries were changed. */
90 SLIST_ENTRY(pagename
) link
;
94 SLIST_HEAD(, pagename
) namelist
; /* Page number to name mappings. */
96 static char format
[MAX_FORMAT_SPEC
]; /* Buffer for scsi cdb format def. */
98 static FILE *edit_file
= NULL
; /* File handle for edit file. */
99 static char edit_path
[] = "/tmp/camXXXXXX";
102 /* Function prototypes. */
103 static void editentry_create(void *, int, void *, int, char *);
104 static void editentry_update(void *, int, void *, int, char *);
105 static int editentry_save(void *, char *);
106 static struct editentry
*editentry_lookup(char *);
107 static int editentry_set(char *, char *, int);
108 static void editlist_populate(struct cam_device
*, int, int, int,
110 static void editlist_save(struct cam_device
*, int, int, int, int,
112 static void nameentry_create(int, char *);
113 static struct pagename
*nameentry_lookup(int);
114 static int load_format(const char *, int);
115 static int modepage_write(FILE *, int);
116 static int modepage_read(FILE *);
117 static void modepage_edit(void);
118 static void modepage_dump(struct cam_device
*, int, int, int, int,
120 static void cleanup_editfile(void);
123 #define returnerr(code) do { \
129 #define RTRIM(string) do { \
131 while (isspace(string[_length = strlen(string) - 1])) \
132 string[_length] = '\0'; \
137 editentry_create(void *hook __unused
, int letter
, void *arg
, int count
,
140 struct editentry
*newentry
; /* Buffer to hold new entry. */
142 /* Allocate memory for the new entry and a copy of the entry name. */
143 if ((newentry
= malloc(sizeof(struct editentry
))) == NULL
||
144 (newentry
->name
= strdup(name
)) == NULL
)
147 /* Trim any trailing whitespace for the entry name. */
148 RTRIM(newentry
->name
);
150 newentry
->editable
= (arg
!= NULL
);
151 newentry
->type
= letter
;
152 newentry
->size
= count
; /* Placeholder; not accurate. */
153 newentry
->value
.svalue
= NULL
;
155 STAILQ_INSERT_TAIL(&editlist
, newentry
, link
);
159 editentry_update(void *hook __unused
, int letter
, void *arg
, int count
,
162 struct editentry
*dest
; /* Buffer to hold entry to update. */
164 dest
= editentry_lookup(name
);
165 assert(dest
!= NULL
);
168 dest
->size
= count
; /* We get the real size now. */
170 switch (dest
->type
) {
171 case 'i': /* Byte-sized integral type. */
172 case 'b': /* Bit-sized integral types. */
174 dest
->value
.ivalue
= (intptr_t)arg
;
177 case 'c': /* Character array. */
178 case 'z': /* Null-padded string. */
179 editentry_set(name
, (char *)arg
, 0);
187 editentry_save(void *hook __unused
, char *name
)
189 struct editentry
*src
; /* Entry value to save. */
191 src
= editentry_lookup(name
);
195 case 'i': /* Byte-sized integral type. */
196 case 'b': /* Bit-sized integral types. */
198 return (src
->value
.ivalue
);
201 case 'c': /* Character array. */
202 case 'z': /* Null-padded string. */
203 return ((intptr_t)src
->value
.svalue
);
210 return (0); /* This should never happen. */
213 static struct editentry
*
214 editentry_lookup(char *name
)
216 struct editentry
*scan
;
218 assert(name
!= NULL
);
220 STAILQ_FOREACH(scan
, &editlist
, link
) {
221 if (strcasecmp(scan
->name
, name
) == 0)
225 /* Not found during list traversal. */
230 editentry_set(char *name
, char *newvalue
, int editonly
)
232 struct editentry
*dest
; /* Modepage entry to update. */
233 char *cval
; /* Pointer to new string value. */
234 char *convertend
; /* End-of-conversion pointer. */
235 int ival
; /* New integral value. */
236 int resolution
; /* Resolution in bits for integer conversion. */
237 int resolution_max
; /* Maximum resolution for modepage's size. */
239 assert(newvalue
!= NULL
);
240 if (*newvalue
== '\0')
241 return (0); /* Nothing to do. */
243 if ((dest
= editentry_lookup(name
)) == NULL
)
245 if (!dest
->editable
&& editonly
)
248 switch (dest
->type
) {
249 case 'i': /* Byte-sized integral type. */
250 case 'b': /* Bit-sized integral types. */
252 /* Convert the value string to an integer. */
253 resolution
= (dest
->type
== 'i')? 8: 1;
254 ival
= (int)strtol(newvalue
, &convertend
, 0);
255 if (*convertend
!= '\0')
259 * Determine the maximum value of the given size for the
260 * current resolution.
261 * XXX Lovely x86's optimize out the case of shifting by 32,
262 * and gcc doesn't currently workaround it (even for int64's),
263 * so we have to kludge it.
265 if (resolution
* dest
->size
== 32)
266 resolution_max
= 0xffffffff;
268 resolution_max
= (1 << (resolution
* dest
->size
)) - 1;
270 if (ival
> resolution_max
|| ival
< 0) {
271 int newival
= (ival
< 0) ? 0 : resolution_max
;
272 warnx("value %d is out of range for entry %s; clipping "
273 "to %d", ival
, name
, newival
);
276 if (dest
->value
.ivalue
!= ival
)
277 editlist_changed
= 1;
278 dest
->value
.ivalue
= ival
;
281 case 'c': /* Character array. */
282 case 'z': /* Null-padded string. */
283 if ((cval
= malloc(dest
->size
+ 1)) == NULL
)
285 bzero(cval
, dest
->size
+ 1);
286 strncpy(cval
, newvalue
, dest
->size
);
287 if (dest
->type
== 'z') {
288 /* Convert trailing spaces to nulls. */
291 for (conv_end
= cval
+ dest
->size
;
292 conv_end
>= cval
; conv_end
--) {
293 if (*conv_end
== ' ')
295 else if (*conv_end
!= '\0')
299 if (strncmp(dest
->value
.svalue
, cval
, dest
->size
) == 0) {
300 /* Nothing changed, free the newly allocated string. */
304 if (dest
->value
.svalue
!= NULL
) {
305 /* Free the current string buffer. */
306 free(dest
->value
.svalue
);
307 dest
->value
.svalue
= NULL
;
309 dest
->value
.svalue
= cval
;
310 editlist_changed
= 1;
321 nameentry_create(int pagenum
, char *name
) {
322 struct pagename
*newentry
;
324 if (pagenum
< 0 || name
== NULL
|| name
[0] == '\0')
327 /* Allocate memory for the new entry and a copy of the entry name. */
328 if ((newentry
= malloc(sizeof(struct pagename
))) == NULL
||
329 (newentry
->name
= strdup(name
)) == NULL
)
332 /* Trim any trailing whitespace for the page name. */
333 RTRIM(newentry
->name
);
335 newentry
->pagenum
= pagenum
;
336 SLIST_INSERT_HEAD(&namelist
, newentry
, link
);
339 static struct pagename
*
340 nameentry_lookup(int pagenum
) {
341 struct pagename
*scan
;
343 SLIST_FOREACH(scan
, &namelist
, link
) {
344 if (pagenum
== scan
->pagenum
)
348 /* Not found during list traversal. */
353 load_format(const char *pagedb_path
, int page
)
356 char str_pagenum
[MAX_PAGENUM_LEN
];
357 char str_pagename
[MAX_PAGENAME_LEN
];
359 int depth
; /* Quoting depth. */
362 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 ((c
= fgetc(pagedb
)) != EOF
) {
400 /* Keep a line count to make error messages more useful. */
403 /* Skip over comments anywhere in the mode database. */
407 } while (c
!= '\n' && c
!= EOF
);
412 /* Strip out newline characters. */
416 /* Keep track of the nesting depth for braces. */
417 if (c
== PAGEDEF_START
)
419 else if (c
== PAGEDEF_END
) {
422 errx(EX_OSFILE
, "%s:%d: %s", pagedb_path
,
423 lineno
, "mismatched bracket");
430 * Locate the page the user is interested in, skipping
434 /* Ignore all whitespace between pages. */
436 } else if (depth
== 0 && c
== PAGEENTRY_END
) {
438 * A page entry terminator will reset page
439 * scanning (useful for assigning names to
440 * modes without providing a mode definition).
442 /* Record the name of this page. */
443 pagenum
= strtol(str_pagenum
, NULL
, 0);
444 nameentry_create(pagenum
, str_pagename
);
446 } else if (depth
== 0 && c
== PAGENAME_START
) {
448 } else if (c
== PAGEDEF_START
) {
449 pagenum
= strtol(str_pagenum
, NULL
, 0);
451 /* Record the name of this page. */
452 nameentry_create(pagenum
, str_pagename
);
454 * Only record the format if this is
455 * the page we are interested in.
457 if (page
== pagenum
&& !found
)
460 } else if (c
== PAGEDEF_END
) {
461 /* Reset the processor state. */
463 } else if (depth
== 0 && ! BUFFERFULL(str_pagenum
)) {
464 strncat(str_pagenum
, &c
, 1);
465 } else if (depth
== 0) {
466 errx(EX_OSFILE
, "%s:%d: %s %d %s", pagedb_path
,
467 lineno
, "page identifier exceeds",
468 sizeof(str_pagenum
) - 1, "characters");
473 if (c
== PAGENAME_END
) {
475 * Return to LOCATE state without resetting the
476 * page number buffer.
479 } else if (! BUFFERFULL(str_pagename
)) {
480 strncat(str_pagename
, &c
, 1);
482 errx(EX_OSFILE
, "%s:%d: %s %d %s", pagedb_path
,
483 lineno
, "page name exceeds",
484 sizeof(str_pagenum
) - 1, "characters");
490 * Transfer the page definition into a format buffer
491 * suitable for use with CDB encoding/decoding routines.
496 } else if (! BUFFERFULL(format
)) {
497 strncat(format
, &c
, 1);
499 errx(EX_OSFILE
, "%s:%d: %s %d %s", pagedb_path
,
500 lineno
, "page definition exceeds",
501 sizeof(format
) - 1, "characters");
509 /* Repeat processing loop with next character. */
513 err(EX_OSFILE
, "%s", pagedb_path
);
515 /* Close the SCSI page database. */
518 if (!found
) /* Never found a matching page. */
525 editlist_populate(struct cam_device
*device
, int modepage
, int page_control
,
526 int dbd
, int retries
, int timeout
)
528 u_int8_t data
[MAX_COMMAND_SIZE
];/* Buffer to hold sense data. */
529 u_int8_t
*mode_pars
; /* Pointer to modepage params. */
530 struct scsi_mode_header_6
*mh
; /* Location of mode header. */
531 struct scsi_mode_page_header
*mph
;
533 STAILQ_INIT(&editlist
);
535 /* Fetch changeable values; use to build initial editlist. */
536 mode_sense(device
, modepage
, 1, dbd
, retries
, timeout
, data
,
539 mh
= (struct scsi_mode_header_6
*)data
;
540 mph
= MODE_PAGE_HEADER(mh
);
541 mode_pars
= MODE_PAGE_DATA(mph
);
543 /* Decode the value data, creating edit_entries for each value. */
544 buff_decode_visit(mode_pars
, mh
->data_length
, format
,
545 editentry_create
, 0);
547 /* Fetch the current/saved values; use to set editentry values. */
548 mode_sense(device
, modepage
, page_control
, dbd
, retries
, timeout
, data
,
550 buff_decode_visit(mode_pars
, mh
->data_length
, format
,
551 editentry_update
, 0);
555 editlist_save(struct cam_device
*device
, int modepage
, int page_control
,
556 int dbd
, int retries
, int timeout
)
558 u_int8_t data
[MAX_COMMAND_SIZE
];/* Buffer to hold sense data. */
559 u_int8_t
*mode_pars
; /* Pointer to modepage params. */
560 struct scsi_mode_header_6
*mh
; /* Location of mode header. */
561 struct scsi_mode_page_header
*mph
;
563 /* Make sure that something changed before continuing. */
564 if (! editlist_changed
)
568 * Preload the CDB buffer with the current mode page data.
569 * XXX If buff_encode_visit would return the number of bytes encoded
570 * we *should* use that to build a header from scratch. As it is
571 * now, we need mode_sense to find out the page length.
573 mode_sense(device
, modepage
, page_control
, dbd
, retries
, timeout
, data
,
576 /* Initial headers & offsets. */
577 mh
= (struct scsi_mode_header_6
*)data
;
578 mph
= MODE_PAGE_HEADER(mh
);
579 mode_pars
= MODE_PAGE_DATA(mph
);
581 /* Encode the value data to be passed back to the device. */
582 buff_encode_visit(mode_pars
, mh
->data_length
, format
,
585 /* Eliminate block descriptors. */
586 bcopy(mph
, ((u_int8_t
*)mh
) + sizeof(*mh
),
587 sizeof(*mph
) + mph
->page_length
);
589 /* Recalculate headers & offsets. */
590 mh
->blk_desc_len
= 0; /* No block descriptors. */
591 mh
->dev_spec
= 0; /* Clear device-specific parameters. */
592 mph
= MODE_PAGE_HEADER(mh
);
593 mode_pars
= MODE_PAGE_DATA(mph
);
595 mph
->page_code
&= SMS_PAGE_CODE
;/* Isolate just the page code. */
596 mh
->data_length
= 0; /* Reserved for MODE SELECT command. */
599 * Write the changes back to the device. If the user editted control
600 * page 3 (saved values) then request the changes be permanently
604 (page_control
<< PAGE_CTRL_SHIFT
== SMS_PAGE_CTRL_SAVED
),
605 retries
, timeout
, (u_int8_t
*)mh
,
606 sizeof(*mh
) + mh
->blk_desc_len
+ sizeof(*mph
) + mph
->page_length
);
610 modepage_write(FILE *file
, int editonly
)
612 struct editentry
*scan
;
615 STAILQ_FOREACH(scan
, &editlist
, link
) {
616 if (scan
->editable
|| !editonly
) {
618 if (scan
->type
== 'c' || scan
->type
== 'z') {
619 fprintf(file
, "%s: %s\n", scan
->name
,
622 fprintf(file
, "%s: %d\n", scan
->name
,
631 modepage_read(FILE *file
)
633 char *buffer
; /* Pointer to dynamic line buffer. */
634 char *line
; /* Pointer to static fgetln buffer. */
635 char *name
; /* Name portion of the line buffer. */
636 char *value
; /* Value portion of line buffer. */
637 size_t length
; /* Length of static fgetln buffer. */
639 #define ABORT_READ(message, param) do { \
640 warnx(message, param); \
645 while ((line
= fgetln(file
, &length
)) != NULL
) {
646 /* Trim trailing whitespace (including optional newline). */
647 while (length
> 0 && isspace(line
[length
- 1]))
650 /* Allocate a buffer to hold the line + terminating null. */
651 if ((buffer
= malloc(length
+ 1)) == NULL
)
653 memcpy(buffer
, line
, length
);
654 buffer
[length
] = '\0';
656 /* Strip out comments. */
657 if ((value
= strchr(buffer
, '#')) != NULL
)
660 /* The name is first in the buffer. Trim whitespace.*/
663 while (isspace(*name
))
666 /* Skip empty lines. */
667 if (strlen(name
) == 0)
670 /* The name ends at the colon; the value starts there. */
671 if ((value
= strrchr(buffer
, ':')) == NULL
)
672 ABORT_READ("no value associated with %s", name
);
673 *value
= '\0'; /* Null-terminate name. */
674 value
++; /* Value starts afterwards. */
676 /* Trim leading and trailing whitespace. */
678 while (isspace(*value
))
681 /* Make sure there is a value left. */
682 if (strlen(value
) == 0)
683 ABORT_READ("no value associated with %s", name
);
685 /* Update our in-memory copy of the modepage entry value. */
686 if (editentry_set(name
, value
, 1) != 0) {
687 if (errno
== ENOENT
) {
688 /* No entry by the name. */
689 ABORT_READ("no such modepage entry \"%s\"",
691 } else if (errno
== EINVAL
) {
693 ABORT_READ("Invalid value for entry \"%s\"",
695 } else if (errno
== ERANGE
) {
696 /* Value out of range for entry type. */
697 ABORT_READ("value out of range for %s", name
);
698 } else if (errno
== EPERM
) {
699 /* Entry is not editable; not fatal. */
700 warnx("modepage entry \"%s\" is read-only; "
707 return (ferror(file
)? -1: 0);
720 if (!isatty(fileno(stdin
))) {
721 /* Not a tty, read changes from stdin. */
722 modepage_read(stdin
);
726 /* Lookup editor to invoke. */
727 if ((editor
= getenv("EDITOR")) == NULL
)
728 editor
= DEFAULT_EDITOR
;
730 /* Create temp file for editor to modify. */
731 if ((fd
= mkstemp(edit_path
)) == -1)
732 errx(EX_CANTCREAT
, "mkstemp failed");
734 atexit(cleanup_editfile
);
736 if ((edit_file
= fdopen(fd
, "w")) == NULL
)
737 err(EX_NOINPUT
, "%s", edit_path
);
739 written
= modepage_write(edit_file
, 1);
745 warnx("no editable entries");
751 * Allocate memory to hold the command line (the 2 extra characters
752 * are to hold the argument separator (a space), and the terminating
755 commandline
= malloc(strlen(editor
) + strlen(edit_path
) + 2);
756 if (commandline
== NULL
)
758 sprintf(commandline
, "%s %s", editor
, edit_path
);
760 /* Invoke the editor on the temp file. */
761 if (system(commandline
) == -1)
762 err(EX_UNAVAILABLE
, "could not invoke %s", editor
);
765 if ((edit_file
= fopen(edit_path
, "r")) == NULL
)
766 err(EX_NOINPUT
, "%s", edit_path
);
768 /* Read any changes made to the temp file. */
769 modepage_read(edit_file
);
775 modepage_dump(struct cam_device
*device
, int page
, int page_control
, int dbd
,
776 int retries
, int timeout
)
778 u_int8_t data
[MAX_COMMAND_SIZE
];/* Buffer to hold sense data. */
779 u_int8_t
*mode_pars
; /* Pointer to modepage params. */
780 struct scsi_mode_header_6
*mh
; /* Location of mode header. */
781 struct scsi_mode_page_header
*mph
;
782 int mode_idx
; /* Index for scanning mode params. */
784 mode_sense(device
, page
, page_control
, dbd
, retries
, timeout
, data
,
787 mh
= (struct scsi_mode_header_6
*)data
;
788 mph
= MODE_PAGE_HEADER(mh
);
789 mode_pars
= MODE_PAGE_DATA(mph
);
791 /* Print the raw mode page data with newlines each 8 bytes. */
792 for (mode_idx
= 0; mode_idx
< mph
->page_length
; mode_idx
++) {
793 printf("%02x%c", mode_pars
[mode_idx
],
794 (((mode_idx
+ 1) % 8) == 0) ? '\n' : ' ');
800 cleanup_editfile(void)
802 if (edit_file
== NULL
)
804 if (fclose(edit_file
) != 0 || unlink(edit_path
) != 0)
805 warn("%s", edit_path
);
810 mode_edit(struct cam_device
*device
, int page
, int page_control
, int dbd
,
811 int edit
, int binary
, int retry_count
, int timeout
)
813 const char *pagedb_path
; /* Path to modepage database. */
816 errx(EX_USAGE
, "cannot edit in binary mode.");
819 if ((pagedb_path
= getenv("SCSI_MODES")) == NULL
)
820 pagedb_path
= DEFAULT_SCSI_MODE_DB
;
822 if (load_format(pagedb_path
, page
) != 0 && (edit
|| verbose
)) {
823 if (errno
== ENOENT
) {
824 /* Modepage database file not found. */
825 warn("cannot open modepage database \"%s\"",
827 } else if (errno
== ESRCH
) {
828 /* Modepage entry not found in database. */
829 warnx("modepage %d not found in database"
830 "\"%s\"", page
, pagedb_path
);
832 /* We can recover in display mode, otherwise we exit. */
834 warnx("reverting to binary display only");
840 editlist_populate(device
, page
, page_control
, dbd
, retry_count
,
845 if (page_control
<< PAGE_CTRL_SHIFT
!= SMS_PAGE_CTRL_CURRENT
&&
846 page_control
<< PAGE_CTRL_SHIFT
!= SMS_PAGE_CTRL_SAVED
)
847 errx(EX_USAGE
, "it only makes sense to edit page 0 "
848 "(current) or page 3 (saved values)");
850 editlist_save(device
, page
, page_control
, dbd
, retry_count
,
852 } else if (binary
|| STAILQ_EMPTY(&editlist
)) {
853 /* Display without formatting information. */
854 modepage_dump(device
, page
, page_control
, dbd
, retry_count
,
857 /* Display with format. */
858 modepage_write(stdout
, 0);
863 mode_list(struct cam_device
*device
, int page_control
, int dbd
,
864 int retry_count
, int timeout
)
866 u_int8_t data
[MAX_COMMAND_SIZE
];/* Buffer to hold sense data. */
867 u_int8_t
*mode_pars
; /* Pointer to modepage params. */
868 struct scsi_mode_header_6
*mh
; /* Location of mode header. */
869 struct scsi_mode_page_header
*mph
;
870 struct pagename
*nameentry
;
871 const char *pagedb_path
;
874 if ((pagedb_path
= getenv("SCSI_MODES")) == NULL
)
875 pagedb_path
= DEFAULT_SCSI_MODE_DB
;
877 if (load_format(pagedb_path
, 0) != 0 && verbose
&& errno
== ENOENT
) {
878 /* Modepage database file not found. */
879 warn("cannot open modepage database \"%s\"", pagedb_path
);
882 /* Build the list of all mode pages by querying the "all pages" page. */
883 mode_sense(device
, SMS_ALL_PAGES_PAGE
, page_control
, dbd
, retry_count
,
884 timeout
, data
, sizeof(data
));
886 mh
= (struct scsi_mode_header_6
*)data
;
887 len
= mh
->blk_desc_len
; /* Skip block descriptors. */
888 /* Iterate through the pages in the reply. */
889 while (len
< mh
->data_length
) {
890 /* Locate the next mode page header. */
891 mph
= (struct scsi_mode_page_header
*)
892 ((intptr_t)mh
+ sizeof(*mh
) + len
);
893 mode_pars
= MODE_PAGE_DATA(mph
);
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
);